Merge branch 'master' into docking

# Conflicts:
#	backends/imgui_impl_sdl3.cpp
#	examples/example_sdl3_vulkan/main.cpp
This commit is contained in:
ocornut
2025-10-23 21:05:22 +02:00
16 changed files with 94 additions and 54 deletions

View File

@@ -138,9 +138,6 @@
#define GLFW_EXPOSE_NATIVE_X11 #define GLFW_EXPOSE_NATIVE_X11
#include <X11/Xatom.h> #include <X11/Xatom.h>
#endif #endif
#ifndef GLFW_EXPOSE_NATIVE_WAYLAND
#define GLFW_EXPOSE_NATIVE_WAYLAND
#endif
#include <GLFW/glfw3native.h> #include <GLFW/glfw3native.h>
#undef Status // X11 headers are leaking this. #undef Status // X11 headers are leaking this.
#endif #endif

View File

@@ -24,6 +24,7 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2025-10-22: Fixed Platform_OpenInShellFn() return value (unused in core).
// 2025-09-24: Skip using the SDL_GetGlobalMouseState() state when one of our window is hovered, as the SDL_EVENT_MOUSE_MOTION data is reliable. Fix macOS notch mouse coordinates issue in fullscreen mode + better perf on X11. (#7919, #7786) // 2025-09-24: Skip using the SDL_GetGlobalMouseState() state when one of our window is hovered, as the SDL_EVENT_MOUSE_MOTION data is reliable. Fix macOS notch mouse coordinates issue in fullscreen mode + better perf on X11. (#7919, #7786)
// 2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown. // 2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown.
// 2025-09-15: Use SDL_GetWindowDisplayScale() on Mac to output DisplayFrameBufferScale. The function is more reliable during resolution changes e.g. going fullscreen. (#8703, #4414) // 2025-09-15: Use SDL_GetWindowDisplayScale() on Mac to output DisplayFrameBufferScale. The function is more reliable during resolution changes e.g. going fullscreen. (#8703, #4414)
@@ -570,7 +571,7 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void
platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText; platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText;
platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText; platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText;
platform_io.Platform_SetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData; platform_io.Platform_SetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData;
platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { return SDL_OpenURL(url) == 0; }; platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { return SDL_OpenURL(url); };
// Update monitor a first time during init // Update monitor a first time during init
ImGui_ImplSDL3_UpdateMonitors(); ImGui_ImplSDL3_UpdateMonitors();

View File

@@ -47,19 +47,38 @@ Other Changes:
result in temporarily incorrect state, which would lead to bugs to side effects result in temporarily incorrect state, which would lead to bugs to side effects
in various locations, e.g. GetContentRegionAvail() calls or using clipper. (#9005) in various locations, e.g. GetContentRegionAvail() calls or using clipper. (#9005)
EndTable() was mistakenly restoring a wrong current table. EndTable() was mistakenly restoring a wrong current table.
- InputText: when buffer is not resizable, trying to paste contents that
cannot fit will now truncate text instead of ignoring the paste. (#9029)
- InputText: avoid continuously overwriting ownership of ImGuiKey_Enter/_KeypadEnter - InputText: avoid continuously overwriting ownership of ImGuiKey_Enter/_KeypadEnter
keys in order to allow e.g. external Shortcut override behavior. (#9004) keys in order to allow e.g. external Shortcut override behavior. (#9004)
- InputText: when using a callback to reduce/manipulate the value of BufTextLen,
we do not require anymore that CursorPos be clamped by user code. (#9029)
- InputTextMultiline: fixed a crash when using ImGuiInputTextFlags_WordWrap and - InputTextMultiline: fixed a crash when using ImGuiInputTextFlags_WordWrap and
resizing the parent window while keeping the multi-line field active (which is resizing the parent window while keeping the multi-line field active (which is
most typically achieved when resizing programmatically or via a docking layout most typically achieved when resizing programmatically or via a docking layout
reacting to a platform window resize). (#3237, #9007) [@anton-kl, @ocornut] reacting to a platform window resize). (#3237, #9007) [@anton-kl, @ocornut]
- Backends: Vulkan: added IMGUI_IMPL_VULKAN_VOLK_FILENAME to configure path to - MultiSelect: added ImGuiMultiSelectFlags_NoSelectOnRightClick to disable default
Volk (default to "volk.h"). (#9008, #7722, #6582, #4854) [@mwlasiuk] right-click processing, which selects item on mouse down and is designed for
- Backends: WebGPU: update to compile with Dawn and Emscripten's 4.0.10+ context-menus. (#8200, #9015)
'--use-port=emdawnwebgpu' ports. (#8381, #8898) [@brutpitt, @trbabb] - Groups: fixed an issue reporting IsItemEdited() signal after EndGroup() when
When using Emscripten 4.0.10+, backend now defaults to IMGUI_IMPL_WEBGPU_BACKEND_DAWN triggered by some widgets e.g. Checkbox(), Selectable() and many others, which
instead of IMGUI_IMPL_WEBGPU_BACKEND_WGPU, if neither are specified. cleared ActiveId at the same time as editing. (#9028)
(note: examples application were not updated yet) Note that IsItemDeactivatedAfterEdit() was not affected, only IsItemEdited).
- Backends:
- GLFW: fixed building on Linux platforms where Wayland headers
are not available. (#9024, #8969, #8921, #8920) [@jagot]
- SDL3: fixed Platform_OpenInShellFn() return value (the return value
was unused in core but might be used by a direct caller). (#9027) [@achabense]
- Vulkan: added IMGUI_IMPL_VULKAN_VOLK_FILENAME to configure path to
Volk (default to "volk.h"). (#9008, #7722, #6582, #4854) [@mwlasiuk]
- WebGPU: update to compile with Dawn and Emscripten's 4.0.10+
'--use-port=emdawnwebgpu' ports. (#8381, #8898) [@brutpitt, @trbabb]
When using Emscripten 4.0.10+, backend now defaults to IMGUI_IMPL_WEBGPU_BACKEND_DAWN
instead of IMGUI_IMPL_WEBGPU_BACKEND_WGPU, if neither are specified.
(note: examples application were not updated yet)
- Examples:
- GLFW+WebGPU: removed unnecessary ImGui_ImplWGPU_InvalidateDeviceObjects() call
during surface resize. (#8381)
Docking+Viewports Branch: Docking+Viewports Branch:

View File

@@ -180,11 +180,7 @@ int main(int, char**)
int width, height; int width, height;
glfwGetFramebufferSize((GLFWwindow*)window, &width, &height); glfwGetFramebufferSize((GLFWwindow*)window, &width, &height);
if (width != wgpu_surface_width || height != wgpu_surface_height) if (width != wgpu_surface_width || height != wgpu_surface_height)
{
ImGui_ImplWGPU_InvalidateDeviceObjects(); // FIXME-OPT: Why doing this? This seems unnecessary and unoptimal.
ResizeSurface(width, height); ResizeSurface(width, height);
ImGui_ImplWGPU_CreateDeviceObjects();
}
// Start the Dear ImGui frame // Start the Dear ImGui frame
ImGui_ImplWGPU_NewFrame(); ImGui_ImplWGPU_NewFrame();

View File

@@ -230,6 +230,7 @@ int main(int, char**)
bool CreateDeviceD3D(HWND hWnd) bool CreateDeviceD3D(HWND hWnd)
{ {
// Setup swap chain // Setup swap chain
// This is a basic setup. Optimally could use e.g. DXGI_SWAP_EFFECT_FLIP_DISCARD and handle fullscreen mode differently. See #8979 for suggestions.
DXGI_SWAP_CHAIN_DESC sd; DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd)); ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 2; sd.BufferCount = 2;

View File

@@ -225,6 +225,7 @@ int main(int, char**)
bool CreateDeviceD3D(HWND hWnd) bool CreateDeviceD3D(HWND hWnd)
{ {
// Setup swap chain // Setup swap chain
// This is a basic setup. Optimally could use e.g. DXGI_SWAP_EFFECT_FLIP_DISCARD and handle fullscreen mode differently. See #8979 for suggestions.
DXGI_SWAP_CHAIN_DESC sd; DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd)); ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 2; sd.BufferCount = 2;

View File

@@ -491,7 +491,7 @@ int main(int, char**)
if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height)) if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height))
{ {
ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount); ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount);
ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, fb_height, fb_height, g_MinImageCount, 0); ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount, 0);
g_MainWindowData.FrameIndex = 0; g_MainWindowData.FrameIndex = 0;
g_SwapChainRebuild = false; g_SwapChainRebuild = false;
} }

View File

@@ -217,6 +217,7 @@ int main(int, char**)
bool CreateDeviceD3D(HWND hWnd) bool CreateDeviceD3D(HWND hWnd)
{ {
// Setup swap chain // Setup swap chain
// This is a basic setup. Optimally could use handle fullscreen mode differently. See #8979 for suggestions.
DXGI_SWAP_CHAIN_DESC sd; DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd)); ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 2; sd.BufferCount = 2;

View File

@@ -219,6 +219,7 @@ int main(int, char**)
bool CreateDeviceD3D(HWND hWnd) bool CreateDeviceD3D(HWND hWnd)
{ {
// Setup swap chain // Setup swap chain
// This is a basic setup. Optimally could use e.g. DXGI_SWAP_EFFECT_FLIP_DISCARD and handle fullscreen mode differently. See #8979 for suggestions.
DXGI_SWAP_CHAIN_DESC sd; DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd)); ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 2; sd.BufferCount = 2;

View File

@@ -332,6 +332,7 @@ int main(int, char**)
bool CreateDeviceD3D(HWND hWnd) bool CreateDeviceD3D(HWND hWnd)
{ {
// Setup swap chain // Setup swap chain
// This is a basic setup. Optimally could handle fullscreen mode differently. See #8979 for suggestions.
DXGI_SWAP_CHAIN_DESC1 sd; DXGI_SWAP_CHAIN_DESC1 sd;
{ {
ZeroMemory(&sd, sizeof(sd)); ZeroMemory(&sd, sizeof(sd));
@@ -545,7 +546,7 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
DXGI_SWAP_CHAIN_DESC1 desc = {}; DXGI_SWAP_CHAIN_DESC1 desc = {};
g_pSwapChain->GetDesc1(&desc); g_pSwapChain->GetDesc1(&desc);
HRESULT result = g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), desc.Format, desc.Flags); HRESULT result = g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), desc.Format, desc.Flags);
assert(SUCCEEDED(result) && "Failed to resize swapchain."); IM_ASSERT(SUCCEEDED(result) && "Failed to resize swapchain.");
CreateRenderTarget(); CreateRenderTarget();
} }
return 0; return 0;

View File

@@ -12197,6 +12197,7 @@ void ImGui::BeginGroup()
group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive; group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
group_data.BackupHoveredIdIsAlive = g.HoveredId != 0; group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;
group_data.BackupIsSameLine = window->DC.IsSameLine; group_data.BackupIsSameLine = window->DC.IsSameLine;
group_data.BackupActiveIdHasBeenEditedThisFrame = g.ActiveIdHasBeenEditedThisFrame;
group_data.BackupDeactivatedIdIsAlive = g.DeactivatedItemData.IsAlive; group_data.BackupDeactivatedIdIsAlive = g.DeactivatedItemData.IsAlive;
group_data.EmitItem = true; group_data.EmitItem = true;
@@ -12261,7 +12262,7 @@ void ImGui::EndGroup()
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
// Forward Edited flag // Forward Edited flag
if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame) if (g.ActiveIdHasBeenEditedThisFrame && !group_data.BackupActiveIdHasBeenEditedThisFrame)
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
// Forward Deactivated flag // Forward Deactivated flag

View File

@@ -29,7 +29,7 @@
// Library Version // Library Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
#define IMGUI_VERSION "1.92.5 WIP" #define IMGUI_VERSION "1.92.5 WIP"
#define IMGUI_VERSION_NUM 19241 #define IMGUI_VERSION_NUM 19242
#define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000
#define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198
#define IMGUI_HAS_VIEWPORT // In 'docking' WIP branch. #define IMGUI_HAS_VIEWPORT // In 'docking' WIP branch.
@@ -3132,6 +3132,7 @@ enum ImGuiMultiSelectFlags_
ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 14, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 14, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection.
//ImGuiMultiSelectFlags_RangeSelect2d = 1 << 15, // Shift+Selection uses 2d geometry instead of linear sequence, so possible to use Shift+up/down to select vertically in grid. Analogous to what BoxSelect does. //ImGuiMultiSelectFlags_RangeSelect2d = 1 << 15, // Shift+Selection uses 2d geometry instead of linear sequence, so possible to use Shift+up/down to select vertically in grid. Analogous to what BoxSelect does.
ImGuiMultiSelectFlags_NavWrapX = 1 << 16, // [Temporary] Enable navigation wrapping on X axis. Provided as a convenience because we don't have a design for the general Nav API for this yet. When the more general feature be public we may obsolete this flag in favor of new one. 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.
}; };
// Main IO structure returned by BeginMultiSelect()/EndMultiSelect(). // Main IO structure returned by BeginMultiSelect()/EndMultiSelect().
@@ -4144,7 +4145,7 @@ struct ImGuiPlatformIO
void* Platform_ClipboardUserData; void* Platform_ClipboardUserData;
// Optional: Open link/folder/file in OS Shell // Optional: Open link/folder/file in OS Shell
// (default to use ShellExecuteW() on Windows, system() on Linux/Mac) // (default to use ShellExecuteW() on Windows, system() on Linux/Mac. expected to return false on failure, but some platforms may always return true)
bool (*Platform_OpenInShellFn)(ImGuiContext* ctx, const char* path); bool (*Platform_OpenInShellFn)(ImGuiContext* ctx, const char* path);
void* Platform_OpenInShellUserData; void* Platform_OpenInShellUserData;
@@ -4235,8 +4236,8 @@ struct ImGuiPlatformIO
// Functions // Functions
//------------------------------------------------------------------ //------------------------------------------------------------------
void ClearPlatformHandlers(); // Clear all Platform_XXX fields. Typically called on Platform Backend shutdown. IMGUI_API void ClearPlatformHandlers(); // Clear all Platform_XXX fields. Typically called on Platform Backend shutdown.
void ClearRendererHandlers(); // Clear all Renderer_XXX fields. Typically called on Renderer Backend shutdown. IMGUI_API void ClearRendererHandlers(); // Clear all Renderer_XXX fields. Typically called on Renderer Backend shutdown.
}; };
// (Optional) This is required when enabling multi-viewport. Represent the bounds of each connected monitor/display and their DPI. // (Optional) This is required when enabling multi-viewport. Represent the bounds of each connected monitor/display and their DPI.

View File

@@ -2731,6 +2731,10 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d
{ {
HelpMarker("Selections can be built using Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data."); HelpMarker("Selections can be built using Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data.");
ImGui::BulletText("Wiki page:");
ImGui::SameLine();
ImGui::TextLinkOpenURL("imgui/wiki/Multi-Select", "https://github.com/ocornut/imgui/wiki/Multi-Select");
// Without any fancy API: manage single-selection yourself. // Without any fancy API: manage single-selection yourself.
IMGUI_DEMO_MARKER("Widgets/Selection State/Single-Select"); IMGUI_DEMO_MARKER("Widgets/Selection State/Single-Select");
if (ImGui::TreeNode("Single-Select")) if (ImGui::TreeNode("Single-Select"))
@@ -3264,6 +3268,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClearOnReselect", &flags, ImGuiMultiSelectFlags_NoAutoClearOnReselect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClearOnReselect", &flags, ImGuiMultiSelectFlags_NoAutoClearOnReselect);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectOnRightClick", &flags, ImGuiMultiSelectFlags_NoSelectOnRightClick);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll);

View File

@@ -1184,6 +1184,7 @@ struct IMGUI_API ImGuiGroupData
ImVec2 BackupCurrLineSize; ImVec2 BackupCurrLineSize;
float BackupCurrLineTextBaseOffset; float BackupCurrLineTextBaseOffset;
ImGuiID BackupActiveIdIsAlive; ImGuiID BackupActiveIdIsAlive;
bool BackupActiveIdHasBeenEditedThisFrame;
bool BackupDeactivatedIdIsAlive; bool BackupDeactivatedIdIsAlive;
bool BackupHoveredIdIsAlive; bool BackupHoveredIdIsAlive;
bool BackupIsSameLine; bool BackupIsSameLine;

View File

@@ -4148,21 +4148,23 @@ static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
obj->TextLen -= n; obj->TextLen -= n;
} }
static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const char* new_text, int new_text_len) static int STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const char* new_text, int new_text_len)
{ {
const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0; const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0;
const int text_len = obj->TextLen; const int text_len = obj->TextLen;
IM_ASSERT(pos <= text_len); IM_ASSERT(pos <= text_len);
if (!is_resizable && (new_text_len + obj->TextLen + 1 > obj->BufCapacity)) // We support partial insertion (with a mod in stb_textedit.h)
return false; const int avail = obj->BufCapacity - 1 - obj->TextLen;
if (!is_resizable && new_text_len > avail)
new_text_len = avail; // 0
if (new_text_len == 0)
return 0;
// Grow internal buffer if needed // Grow internal buffer if needed
IM_ASSERT(obj->TextSrc == obj->TextA.Data); IM_ASSERT(obj->TextSrc == obj->TextA.Data);
if (new_text_len + text_len + 1 > obj->TextA.Size) if (text_len + new_text_len + 1 > obj->TextA.Size && is_resizable)
{ {
if (!is_resizable)
return false;
obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1); obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1);
obj->TextSrc = obj->TextA.Data; obj->TextSrc = obj->TextA.Data;
} }
@@ -4176,7 +4178,7 @@ static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ch
obj->TextLen += new_text_len; obj->TextLen += new_text_len;
obj->TextA[obj->TextLen] = '\0'; obj->TextA[obj->TextLen] = '\0';
return true; return new_text_len;
} }
// We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) // We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols)
@@ -4211,7 +4213,8 @@ static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* st
state->cursor = state->select_start = state->select_end = 0; state->cursor = state->select_start = state->select_end = 0;
if (text_len <= 0) if (text_len <= 0)
return; return;
if (ImStb::STB_TEXTEDIT_INSERTCHARS(str, 0, text, text_len)) int text_len_inserted = ImStb::STB_TEXTEDIT_INSERTCHARS(str, 0, text, text_len);
if (text_len_inserted > 0)
{ {
state->cursor = state->select_start = state->select_end = text_len; state->cursor = state->select_start = state->select_end = text_len;
state->has_preferred_x = 0; state->has_preferred_x = 0;
@@ -4306,15 +4309,20 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
ImGuiContext& g = *Ctx; ImGuiContext& g = *Ctx;
ImGuiInputTextState* obj = &g.InputTextState; ImGuiInputTextState* obj = &g.InputTextState;
IM_ASSERT(obj->ID != 0 && g.ActiveId == obj->ID); IM_ASSERT(obj->ID != 0 && g.ActiveId == obj->ID);
const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0;
const bool is_readonly = (Flags & ImGuiInputTextFlags_ReadOnly) != 0;
int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)ImStrlen(new_text);
// We support partial insertion (with a mod in stb_textedit.h)
const int avail = BufSize - 1 - BufTextLen;
if (!is_resizable && new_text_len > avail)
new_text_len = avail; // 0
if (new_text_len == 0)
return;
// Grow internal buffer if needed // Grow internal buffer if needed
const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0; if (new_text_len + BufTextLen + 1 > obj->TextA.Size && is_resizable && !is_readonly)
const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)ImStrlen(new_text);
if (new_text_len + BufTextLen + 1 > obj->TextA.Size && (Flags & ImGuiInputTextFlags_ReadOnly) == 0)
{ {
if (!is_resizable)
return;
IM_ASSERT(Buf == obj->TextA.Data); IM_ASSERT(Buf == obj->TextA.Data);
int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1; int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
obj->TextA.resize(new_buf_size + 1); obj->TextA.resize(new_buf_size + 1);
@@ -5268,10 +5276,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
callback_data.BufTextLen = state->TextLen; callback_data.BufTextLen = state->TextLen;
callback_data.BufSize = state->BufCapacity; callback_data.BufSize = state->BufCapacity;
callback_data.BufDirty = false; callback_data.BufDirty = false;
callback_data.CursorPos = state->Stb->cursor;
const int utf8_cursor_pos = callback_data.CursorPos = state->Stb->cursor; callback_data.SelectionStart = state->Stb->select_start;
const int utf8_selection_start = callback_data.SelectionStart = state->Stb->select_start; callback_data.SelectionEnd = state->Stb->select_end;
const int utf8_selection_end = callback_data.SelectionEnd = state->Stb->select_end;
// Call user code // Call user code
callback(&callback_data); callback(&callback_data);
@@ -5281,11 +5288,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
IM_ASSERT(callback_data.Buf == callback_buf); // Invalid to modify those fields IM_ASSERT(callback_data.Buf == callback_buf); // Invalid to modify those fields
IM_ASSERT(callback_data.BufSize == state->BufCapacity); IM_ASSERT(callback_data.BufSize == state->BufCapacity);
IM_ASSERT(callback_data.Flags == flags); IM_ASSERT(callback_data.Flags == flags);
const bool buf_dirty = callback_data.BufDirty; if (callback_data.BufDirty || callback_data.CursorPos != state->Stb->cursor)
if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty) { state->Stb->cursor = callback_data.CursorPos; state->CursorFollow = true; } state->CursorFollow = true;
if (callback_data.SelectionStart != utf8_selection_start || buf_dirty) { state->Stb->select_start = (callback_data.SelectionStart == callback_data.CursorPos) ? state->Stb->cursor : callback_data.SelectionStart; } state->Stb->cursor = ImClamp(callback_data.CursorPos, 0, callback_data.BufTextLen);
if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty) { state->Stb->select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb->select_start : callback_data.SelectionEnd; } state->Stb->select_start = ImClamp(callback_data.SelectionStart, 0, callback_data.BufTextLen);
if (buf_dirty) state->Stb->select_end = ImClamp(callback_data.SelectionEnd, 0, callback_data.BufTextLen);
if (callback_data.BufDirty)
{ {
// Callback may update buffer and thus set buf_dirty even in read-only mode. // Callback may update buffer and thus set buf_dirty even in read-only mode.
IM_ASSERT(callback_data.BufTextLen == (int)ImStrlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! IM_ASSERT(callback_data.BufTextLen == (int)ImStrlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
@@ -8219,8 +8227,8 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
} }
// Right-click handling. // Right-click handling.
// FIXME-MULTISELECT: Currently filtered out by ImGuiMultiSelectFlags_NoAutoSelect but maybe should be moved to Selectable(). See https://github.com/ocornut/imgui/pull/5816 // FIXME-MULTISELECT: Maybe should be moved to Selectable()? Also see #5816, #8200, #9015
if (hovered && IsMouseClicked(1) && (flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) if (hovered && IsMouseClicked(1) && (flags & (ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoSelectOnRightClick)) == 0)
{ {
if (g.ActiveId != 0 && g.ActiveId != id) if (g.ActiveId != 0 && g.ActiveId != id)
ClearActiveID(); ClearActiveID();

View File

@@ -5,7 +5,8 @@
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783) // - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
// - Added name to struct or it may be forward declared in our code. // - Added name to struct or it may be forward declared in our code.
// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925) // - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
// Grep for [DEAR IMGUI] to find the changes. // - Changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
// Grep for [DEAR IMGUI] to find some changes.
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_* // - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
// stb_textedit.h - v1.14 - public domain - Sean Barrett // stb_textedit.h - v1.14 - public domain - Sean Barrett
@@ -39,6 +40,7 @@
// //
// VERSION HISTORY // VERSION HISTORY
// //
// !!!! (2025-10-23) changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
// 1.14 (2021-07-11) page up/down, various fixes // 1.14 (2021-07-11) page up/down, various fixes
// 1.13 (2019-02-07) fix bug in undo size management // 1.13 (2019-02-07) fix bug in undo size management
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash // 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
@@ -147,7 +149,8 @@
// as manually wordwrapping for end-of-line positioning // as manually wordwrapping for end-of-line positioning
// //
// STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i // STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i
// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) // STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) try to insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*)
// returns number of characters actually inserted. [DEAR IMGUI]
// //
// STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key // STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key
// //
@@ -775,7 +778,8 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
stb_textedit_delete_selection(str,state); stb_textedit_delete_selection(str,state);
// try to insert the characters // try to insert the characters
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) { len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len);
if (len) {
stb_text_makeundo_insert(state, state->cursor, len); stb_text_makeundo_insert(state, state->cursor, len);
state->cursor += len; state->cursor += len;
state->has_preferred_x = 0; state->has_preferred_x = 0;
@@ -800,13 +804,15 @@ static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* sta
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
stb_text_makeundo_replace(str, state, state->cursor, 1, 1); stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) { text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len);
if (text_len) {
state->cursor += text_len; state->cursor += text_len;
state->has_preferred_x = 0; state->has_preferred_x = 0;
} }
} else { } else {
stb_textedit_delete_selection(str, state); // implicitly clamps stb_textedit_delete_selection(str, state); // implicitly clamps
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) { text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len);
if (text_len) {
stb_text_makeundo_insert(state, state->cursor, text_len); stb_text_makeundo_insert(state, state->cursor, text_len);
state->cursor += text_len; state->cursor += text_len;
state->has_preferred_x = 0; state->has_preferred_x = 0;
@@ -1352,7 +1358,7 @@ static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// check type of recorded action: // check type of recorded action:
if (u.insert_length) { if (u.insert_length) {
// easy case: was a deletion, so we need to insert n characters // easy case: was a deletion, so we need to insert n characters
STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); u.insert_length = STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length);
s->undo_char_point -= u.insert_length; s->undo_char_point -= u.insert_length;
} }
@@ -1403,7 +1409,7 @@ static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
if (r.insert_length) { if (r.insert_length) {
// easy case: need to insert n characters // easy case: need to insert n characters
STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); r.insert_length = STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length);
s->redo_char_point += r.insert_length; s->redo_char_point += r.insert_length;
} }