Merge branch 'master' into docking

This commit is contained in:
ocornut
2025-09-08 13:35:24 +02:00
14 changed files with 407 additions and 201 deletions

View File

@@ -35,9 +35,20 @@ jobs:
Expand-Archive -Path SDL3-devel-3.2.18-VC.zip Expand-Archive -Path SDL3-devel-3.2.18-VC.zip
echo "SDL3_DIR=$(pwd)\SDL3-devel-3.2.18-VC\SDL3-3.2.18\" >>${env:GITHUB_ENV} echo "SDL3_DIR=$(pwd)\SDL3-devel-3.2.18-VC\SDL3-3.2.18\" >>${env:GITHUB_ENV}
Invoke-WebRequest -Uri "https://github.com/ocornut/imgui/files/3789205/vulkan-sdk-1.1.121.2.zip" -OutFile vulkan-sdk-1.1.121.2.zip # VulkanSDK (retrieve minimal bits of the SDK from git)
Expand-Archive -Path vulkan-sdk-1.1.121.2.zip $vulkanVersion = "1.4.326"
echo "VULKAN_SDK=$(pwd)\vulkan-sdk-1.1.121.2\" >>${env:GITHUB_ENV} # 1. Get the vulkan headers, we will treat that folder as the sdk folder to avoid having to copy headers around
Invoke-WebRequest -Uri "https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/v$($vulkanVersion).zip" -OutFile Vulkan-Headers-$($vulkanVersion).zip
Expand-Archive -Path Vulkan-Headers-$($vulkanVersion).zip
echo "VULKAN_SDK=$(pwd)\Vulkan-Headers-$($vulkanVersion)\Vulkan-Headers-$($vulkanVersion)" >>${env:GITHUB_ENV}
# 2. Get and build the vulkan loader source code (UPDATE_DEPS=On will make it automatically fetch its dependencies)
Invoke-WebRequest -Uri "https://github.com/KhronosGroup/Vulkan-Loader/archive/refs/tags/v$($vulkanVersion).zip" -OutFile Vulkan-Loader-$($vulkanVersion).zip
Expand-Archive -Path Vulkan-Loader-$($vulkanVersion).zip
cmake -S Vulkan-Loader-$($vulkanVersion)\Vulkan-Loader-$($vulkanVersion) -B VulkanLoader-build -D UPDATE_DEPS=On
cmake --build VulkanLoader-build
# 3. Copy the built lib/dll to the expected place
mkdir Vulkan-Headers-$($vulkanVersion)\Vulkan-Headers-$($vulkanVersion)\Lib
copy VulkanLoader-build\loader\Debug\vulkan-1.* Vulkan-Headers-$($vulkanVersion)\Vulkan-Headers-$($vulkanVersion)\Lib\
- name: Fix Projects - name: Fix Projects
shell: powershell shell: powershell

View File

@@ -619,7 +619,7 @@ void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window)
bd->PrevUserCallbackMonitor = nullptr; bd->PrevUserCallbackMonitor = nullptr;
} }
// Set to 'true' to enable chaining installed callbacks for all windows (including secondary viewports created by backends or by user. // Set to 'true' to enable chaining installed callbacks for all windows (including secondary viewports created by backends or by user).
// This is 'false' by default meaning we only chain callbacks for the main viewport. // This is 'false' by default meaning we only chain callbacks for the main viewport.
// We cannot set this to 'true' by default because user callbacks code may be not testing the 'window' parameter of their callback. // We cannot set this to 'true' by default because user callbacks code may be not testing the 'window' parameter of their callback.
// If you set this to 'true' your user callback code will need to make sure you are testing the 'window' parameter. // If you set this to 'true' your user callback code will need to make sure you are testing the 'window' parameter.

View File

@@ -87,7 +87,7 @@ static ImGui_ImplSDLGPU3_Data* ImGui_ImplSDLGPU3_GetBackendData()
static void ImGui_ImplSDLGPU3_SetupRenderState(ImDrawData* draw_data, ImGui_ImplSDLGPU3_RenderState* render_state, SDL_GPUGraphicsPipeline* pipeline, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass, ImGui_ImplSDLGPU3_FrameData* fd, uint32_t fb_width, uint32_t fb_height) static void ImGui_ImplSDLGPU3_SetupRenderState(ImDrawData* draw_data, ImGui_ImplSDLGPU3_RenderState* render_state, SDL_GPUGraphicsPipeline* pipeline, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass, ImGui_ImplSDLGPU3_FrameData* fd, uint32_t fb_width, uint32_t fb_height)
{ {
ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData();
render_state->SamplerCurrent = render_state->SamplerCurrent = bd->TexSampler; render_state->SamplerCurrent = bd->TexSampler;
// Bind graphics pipeline // Bind graphics pipeline
SDL_BindGPUGraphicsPipeline(render_pass, pipeline); SDL_BindGPUGraphicsPipeline(render_pass, pipeline);

View File

@@ -123,7 +123,7 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V
void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator);
// Vulkan prototypes for use with custom loaders // Vulkan prototypes for use with custom loaders
// (see description of IMGUI_IMPL_VULKAN_NO_PROTOTYPES in imgui_impl_vulkan.h // (see description of IMGUI_IMPL_VULKAN_NO_PROTOTYPES in imgui_impl_vulkan.h)
#if defined(VK_NO_PROTOTYPES) && !defined(VOLK_H_) #if defined(VK_NO_PROTOTYPES) && !defined(VOLK_H_)
#define IMGUI_IMPL_VULKAN_USE_LOADER #define IMGUI_IMPL_VULKAN_USE_LOADER
static bool g_FunctionsLoaded = false; static bool g_FunctionsLoaded = false;
@@ -1460,7 +1460,7 @@ void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulk
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// Internal / Miscellaneous Vulkan Helpers // Internal / Miscellaneous Vulkan Helpers
// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own app.) // (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.)
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// You probably do NOT need to use or care about those functions. // You probably do NOT need to use or care about those functions.
// Those functions only exist because: // Those functions only exist because:
@@ -1470,7 +1470,7 @@ void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulk
// but it is too much code to duplicate everywhere so we exceptionally expose them. // but it is too much code to duplicate everywhere so we exceptionally expose them.
// //
// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.). // Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.).
// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work. // You may read this code to learn about Vulkan, but it is recommended you use your own custom tailored code to do equivalent work.
// (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions) // (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions)
//------------------------------------------------------------------------- //-------------------------------------------------------------------------

View File

@@ -125,7 +125,7 @@ struct ImGui_ImplVulkan_MainPipelineCreateInfo
VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; // Optional, valid if .sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; // Optional, valid if .sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR
#endif #endif
}; };
IMGUI_IMPL_API void ImGui_ImplVulkan_CreateMainPipeline(const ImGui_ImplVulkan_MainPipelineCreateInfo& info); // (render_pass xor (p_dynamic_rendering && p_dynamic_rendering is correct (sType and pNext)) IMGUI_IMPL_API void ImGui_ImplVulkan_CreateMainPipeline(const ImGui_ImplVulkan_MainPipelineCreateInfo& info); // (render_pass xor (p_dynamic_rendering && p_dynamic_rendering is correct (sType and pNext)))
// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually. // (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually.
IMGUI_IMPL_API void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex); IMGUI_IMPL_API void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex);
@@ -164,12 +164,12 @@ struct ImGui_ImplVulkan_RenderState
// //
// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, // Your engine/app will likely _already_ have code to setup all that stuff (swap chain,
// render pass, frame buffers, etc.). You may read this code if you are curious, but // render pass, frame buffers, etc.). You may read this code if you are curious, but
// it is recommended you use you own custom tailored code to do equivalent work. // it is recommended you use your own custom tailored code to do equivalent work.
// //
// We don't provide a strong guarantee that we won't change those functions API. // We don't provide a strong guarantee that we won't change those functions API.
// //
// The ImGui_ImplVulkanH_XXX functions should NOT interact with any of the state used // The ImGui_ImplVulkanH_XXX functions should NOT interact with any of the state used
// by the regular ImGui_ImplVulkan_XXX functions). // by the regular ImGui_ImplVulkan_XXX functions.
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
struct ImGui_ImplVulkanH_Frame; struct ImGui_ImplVulkanH_Frame;

View File

@@ -183,6 +183,7 @@ Other Changes:
to play nice with -fsanitize=undefined. (#8874) [@i25e] to play nice with -fsanitize=undefined. (#8874) [@i25e]
- CI: Added SDL3 builds to MacOS and Windows. (#8819, #8778) [@scribam] - CI: Added SDL3 builds to MacOS and Windows. (#8819, #8778) [@scribam]
- CI: Updated Windows CI to use a more recent SDL2. (#8819, #8778) [@scribam] - CI: Updated Windows CI to use a more recent SDL2. (#8819, #8778) [@scribam]
- CI: Updates Windows CI to use a more recent VulkanSDK. (#8925, #8778) [@yaz0r]
- Examples: SDL3+Metal: added SDL3+Metal example. (#8827, #8825) [@shi-yan] - Examples: SDL3+Metal: added SDL3+Metal example. (#8827, #8825) [@shi-yan]
- Examples: SDL3+SDL_GPU: use SDL_WaitAndAcquireGPUSwapchainTexture() instead - Examples: SDL3+SDL_GPU: use SDL_WaitAndAcquireGPUSwapchainTexture() instead
of SDL_AcquireGPUSwapchainTexture(). (#8830) [@itsdanott] of SDL_AcquireGPUSwapchainTexture(). (#8830) [@itsdanott]
@@ -385,7 +386,7 @@ Breaking changes:
to 4096 but that limit isn't necessary anymore, and Renderer_TextureMaxWidth covers this) to 4096 but that limit isn't necessary anymore, and Renderer_TextureMaxWidth covers this)
However you may set TexMinWidth = TexMaxWidth for the same effect. However you may set TexMinWidth = TexMaxWidth for the same effect.
- Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on - Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on
ImGuiContext to create one, you'll need to call ImFontAtlasUpdateNewFrame() yourself. ImGuiContext to create one), you'll need to call ImFontAtlasUpdateNewFrame() yourself.
An assert will trigger if you don't. An assert will trigger if you don't.
- Fonts: obsoleted ImGui::SetWindowFontScale() which is not useful anymore. Prefer using - Fonts: obsoleted ImGui::SetWindowFontScale() which is not useful anymore. Prefer using
PushFont(NULL, style.FontSizeBase * factor) or to manipulate other scaling factors. PushFont(NULL, style.FontSizeBase * factor) or to manipulate other scaling factors.
@@ -1243,7 +1244,7 @@ Breaking changes:
allows casting any pointer/integer type without warning: allows casting any pointer/integer type without warning:
- May warn: ImGui::Image((void*)MyTextureData, ...); - May warn: ImGui::Image((void*)MyTextureData, ...);
- May warn: ImGui::Image((void*)(intptr_t)MyTextureData, ...); - May warn: ImGui::Image((void*)(intptr_t)MyTextureData, ...);
- Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData), ...); - Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData, ...);
- Note that you can always define ImTextureID to be your own high-level structures - Note that you can always define ImTextureID to be your own high-level structures
(with dedicated constructors and extra render parameters) if you like. (with dedicated constructors and extra render parameters) if you like.
- IO: moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool. - IO: moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
@@ -5053,7 +5054,7 @@ Breaking Changes:
- ShowTestWindow() -> use ShowDemoWindow() - ShowTestWindow() -> use ShowDemoWindow()
- IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow) - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
- IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
- SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f) - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f))
- GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing() - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing()
- ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg
- ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding

View File

@@ -454,7 +454,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures:
- Fonts: ImFontConfig::OversampleH/OversampleV default to automatic (== 0) since v1.91.8. It is quite important you keep it automatic until we decide if we want to provide a way to express finer policy, otherwise you will likely waste texture space when using large glyphs. Note that the imgui_freetype backend doesn't use and does not need oversampling. - Fonts: ImFontConfig::OversampleH/OversampleV default to automatic (== 0) since v1.91.8. It is quite important you keep it automatic until we decide if we want to provide a way to express finer policy, otherwise you will likely waste texture space when using large glyphs. Note that the imgui_freetype backend doesn't use and does not need oversampling.
- Fonts: specifying glyph ranges is now unnecessary. The value of ImFontConfig::GlyphRanges[] is only useful for legacy backends. All GetGlyphRangesXXXX() functions are now marked obsolete: GetGlyphRangesDefault(), GetGlyphRangesGreek(), GetGlyphRangesKorean(), GetGlyphRangesJapanese(), GetGlyphRangesChineseSimplifiedCommon(), GetGlyphRangesChineseFull(), GetGlyphRangesCyrillic(), GetGlyphRangesThai(), GetGlyphRangesVietnamese(). - Fonts: specifying glyph ranges is now unnecessary. The value of ImFontConfig::GlyphRanges[] is only useful for legacy backends. All GetGlyphRangesXXXX() functions are now marked obsolete: GetGlyphRangesDefault(), GetGlyphRangesGreek(), GetGlyphRangesKorean(), GetGlyphRangesJapanese(), GetGlyphRangesChineseSimplifiedCommon(), GetGlyphRangesChineseFull(), GetGlyphRangesCyrillic(), GetGlyphRangesThai(), GetGlyphRangesVietnamese().
- Fonts: removed ImFontAtlas::TexDesiredWidth to enforce a texture width. (#327) - Fonts: removed ImFontAtlas::TexDesiredWidth to enforce a texture width. (#327)
- Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on ImGuiContext to create one, you'll need to call ImFontAtlasUpdateNewFrame() yourself. An assert will trigger if you don't. - Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on ImGuiContext to create one), you'll need to call ImFontAtlasUpdateNewFrame() yourself. An assert will trigger if you don't.
- Fonts: obsolete ImGui::SetWindowFontScale() which is not useful anymore. Prefer using 'PushFont(NULL, style.FontSizeBase * factor)' or to manipulate other scaling factors. - Fonts: obsolete ImGui::SetWindowFontScale() which is not useful anymore. Prefer using 'PushFont(NULL, style.FontSizeBase * factor)' or to manipulate other scaling factors.
- Fonts: obsoleted ImFont::Scale which is not useful anymore. - Fonts: obsoleted ImFont::Scale which is not useful anymore.
- Fonts: generally reworked Internals of ImFontAtlas and ImFont. While in theory a vast majority of users shouldn't be affected, some use cases or extensions might be. Among other things: - Fonts: generally reworked Internals of ImFontAtlas and ImFont. While in theory a vast majority of users shouldn't be affected, some use cases or extensions might be. Among other things:
@@ -536,7 +536,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures:
in doubt it is almost always better to do an intermediate intptr_t cast, since it allows casting any pointer/integer type without warning: in doubt it is almost always better to do an intermediate intptr_t cast, since it allows casting any pointer/integer type without warning:
- May warn: ImGui::Image((void*)MyTextureData, ...); - May warn: ImGui::Image((void*)MyTextureData, ...);
- May warn: ImGui::Image((void*)(intptr_t)MyTextureData, ...); - May warn: ImGui::Image((void*)(intptr_t)MyTextureData, ...);
- Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData), ...); - Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData, ...);
- note that you can always define ImTextureID to be your own high-level structures (with dedicated constructors) if you like. - note that you can always define ImTextureID to be your own high-level structures (with dedicated constructors) if you like.
- 2024/10/03 (1.91.3) - drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is a still special value due to legacy reasons, unless using ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76) - 2024/10/03 (1.91.3) - drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is a still special value due to legacy reasons, unless using ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76)
- drags: extended behavior of ImGuiSliderFlags_AlwaysClamp to include _ClampZeroRange. It considers v_min==v_max==0.0f as a valid clamping range (aka edits not allowed). - drags: extended behavior of ImGuiSliderFlags_AlwaysClamp to include _ClampZeroRange. It considers v_min==v_max==0.0f as a valid clamping range (aka edits not allowed).
@@ -826,7 +826,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures:
- ShowTestWindow() -> use ShowDemoWindow() - ShowTestWindow() -> use ShowDemoWindow()
- IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow) - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
- IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
- SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f) - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f))
- GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing() - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing()
- ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg
- ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding
@@ -7757,7 +7757,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
} }
// Process SetNextWindow***() calls // Process SetNextWindow***() calls
// (FIXME: Consider splitting the HasXXX flags into X/Y components // (FIXME: Consider splitting the HasXXX flags into X/Y components)
bool window_pos_set_by_api = false; bool window_pos_set_by_api = false;
bool window_size_x_set_by_api = false, window_size_y_set_by_api = false; bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos) if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos)
@@ -9042,7 +9042,7 @@ void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& s
} }
// Content size = inner scrollable rectangle, padded with WindowPadding. // Content size = inner scrollable rectangle, padded with WindowPadding.
// SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item. // SetNextWindowContentSize(ImVec2(100,100)) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
void ImGui::SetNextWindowContentSize(const ImVec2& size) void ImGui::SetNextWindowContentSize(const ImVec2& size)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
@@ -23165,7 +23165,7 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
} }
const ImVec2* pr = window->NavPreferredScoringPosRel; const ImVec2* pr = window->NavPreferredScoringPosRel;
for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++) for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
BulletText("NavPreferredScoringPosRel[%d] = {%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater. BulletText("NavPreferredScoringPosRel[%d] = (%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater.
BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
BulletText("Viewport: %d%s, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportOwned ? " (Owned)" : "", window->ViewportId, window->ViewportPos.x, window->ViewportPos.y); BulletText("Viewport: %d%s, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportOwned ? " (Owned)" : "", window->ViewportId, window->ViewportPos.x, window->ViewportPos.y);

25
imgui.h
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.3 WIP" #define IMGUI_VERSION "1.92.3 WIP"
#define IMGUI_VERSION_NUM 19225 #define IMGUI_VERSION_NUM 19226
#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.
@@ -237,6 +237,7 @@ typedef int ImGuiTableBgTarget; // -> enum ImGuiTableBgTarget_ // Enum: A
// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments. // - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
typedef int ImDrawFlags; // -> enum ImDrawFlags_ // Flags: for ImDrawList functions typedef int ImDrawFlags; // -> enum ImDrawFlags_ // Flags: for ImDrawList functions
typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList instance typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList instance
typedef int ImDrawTextFlags; // -> enum ImDrawTextFlags_ // Internal, do not use!
typedef int ImFontFlags; // -> enum ImFontFlags_ // Flags: for ImFont typedef int ImFontFlags; // -> enum ImFontFlags_ // Flags: for ImFont
typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas
typedef int ImGuiBackendFlags; // -> enum ImGuiBackendFlags_ // Flags: for io.BackendFlags typedef int ImGuiBackendFlags; // -> enum ImGuiBackendFlags_ // Flags: for io.BackendFlags
@@ -1084,8 +1085,8 @@ namespace ImGui
// Inputs Utilities: Shortcut Testing & Routing [BETA] // Inputs Utilities: Shortcut Testing & Routing [BETA]
// - ImGuiKeyChord = a ImGuiKey + optional ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super. // - ImGuiKeyChord = a ImGuiKey + optional ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super.
// ImGuiKey_C // Accepted by functions taking ImGuiKey or ImGuiKeyChord arguments) // ImGuiKey_C // Accepted by functions taking ImGuiKey or ImGuiKeyChord arguments
// ImGuiMod_Ctrl | ImGuiKey_C // Accepted by functions taking ImGuiKeyChord arguments) // ImGuiMod_Ctrl | ImGuiKey_C // Accepted by functions taking ImGuiKeyChord arguments
// only ImGuiMod_XXX values are legal to combine with an ImGuiKey. You CANNOT combine two ImGuiKey values. // only ImGuiMod_XXX values are legal to combine with an ImGuiKey. You CANNOT combine two ImGuiKey values.
// - The general idea is that several callers may register interest in a shortcut, and only one owner gets it. // - The general idea is that several callers may register interest in a shortcut, and only one owner gets it.
// Parent -> call Shortcut(Ctrl+S) // When Parent is focused, Parent gets the shortcut. // Parent -> call Shortcut(Ctrl+S) // When Parent is focused, Parent gets the shortcut.
@@ -1224,7 +1225,7 @@ enum ImGuiWindowFlags_
}; };
// Flags for ImGui::BeginChild() // Flags for ImGui::BeginChild()
// (Legacy: bit 0 must always correspond to ImGuiChildFlags_Borders to be backward compatible with old API using 'bool border = false'. // (Legacy: bit 0 must always correspond to ImGuiChildFlags_Borders to be backward compatible with old API using 'bool border = false'.)
// About using AutoResizeX/AutoResizeY flags: // About using AutoResizeX/AutoResizeY flags:
// - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see "Demo->Child->Auto-resize with Constraints"). // - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see "Demo->Child->Auto-resize with Constraints").
// - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing. // - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing.
@@ -2529,7 +2530,7 @@ struct ImGuiIO
// Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro. // Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro.
// - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability. // - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability.
// - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application. // - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.
// e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version). // e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version.
bool ConfigDebugIsDebuggerPresent; // = false // Enable various tools calling IM_DEBUG_BREAK(). bool ConfigDebugIsDebuggerPresent; // = false // Enable various tools calling IM_DEBUG_BREAK().
// Tools to detect code submitting items with conflicting/duplicate IDs // Tools to detect code submitting items with conflicting/duplicate IDs
@@ -2634,7 +2635,7 @@ struct ImGuiIO
bool KeySuper; // Keyboard modifier down: Windows/Super (non-macOS), Ctrl (macOS) bool KeySuper; // Keyboard modifier down: Windows/Super (non-macOS), Ctrl (macOS)
// Other state maintained from data above + IO function calls // Other state maintained from data above + IO function calls
ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags. Read-only, updated by NewFrame() ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags). Read-only, updated by NewFrame()
ImGuiKeyData KeysData[ImGuiKey_NamedKey_COUNT];// Key state for all known keys. MUST use 'key - ImGuiKey_NamedKey_BEGIN' as index. Use IsKeyXXX() functions to access this. ImGuiKeyData KeysData[ImGuiKey_NamedKey_COUNT];// Key state for all known keys. MUST use 'key - ImGuiKey_NamedKey_BEGIN' as index. Use IsKeyXXX() functions to access this.
bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup. bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup.
ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid)
@@ -2712,10 +2713,10 @@ struct ImGuiInputTextCallbackData
ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History] ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History]
char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer! char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer!
int BufTextLen; // Text length (in bytes) // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length() int BufTextLen; // Text length (in bytes) // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length()
int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1 int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land: == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1
bool BufDirty; // Set if you modify Buf/BufTextLen! // Write // [Completion,History,Always] bool BufDirty; // Set if you modify Buf/BufTextLen! // Write // [Completion,History,Always]
int CursorPos; // // Read-write // [Completion,History,Always] int CursorPos; // // Read-write // [Completion,History,Always]
int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection) int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection
int SelectionEnd; // // Read-write // [Completion,History,Always] int SelectionEnd; // // Read-write // [Completion,History,Always]
// Helper functions for text manipulation. // Helper functions for text manipulation.
@@ -3254,7 +3255,7 @@ struct ImDrawCmd
// Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature) // Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature)
// Since 1.92: removed ImDrawCmd::TextureId field, the getter function must be used! // Since 1.92: removed ImDrawCmd::TextureId field, the getter function must be used!
inline ImTextureID GetTexID() const; // == (TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID inline ImTextureID GetTexID() const; // == (TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID)
}; };
// Vertex layout // Vertex layout
@@ -3751,7 +3752,7 @@ struct ImFontAtlas
// - User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID(). // - User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID().
// - The pitch is always = Width * BytesPerPixels (1 or 4) // - The pitch is always = Width * BytesPerPixels (1 or 4)
// - Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into // - Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into
// the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste. // the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste).
// - From 1.92 with backends supporting ImGuiBackendFlags_RendererHasTextures: // - From 1.92 with backends supporting ImGuiBackendFlags_RendererHasTextures:
// - Calling Build(), GetTexDataAsAlpha8(), GetTexDataAsRGBA32() is not needed. // - Calling Build(), GetTexDataAsAlpha8(), GetTexDataAsRGBA32() is not needed.
// - In backend: replace calls to ImFontAtlas::SetTexID() with calls to ImTextureData::SetTexID() after honoring texture creation. // - In backend: replace calls to ImFontAtlas::SetTexID() with calls to ImTextureData::SetTexID() after honoring texture creation.
@@ -3866,7 +3867,7 @@ struct ImFontAtlas
IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyphForSize(ImFont* font, float font_size, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0)); // ADDED AND OBSOLETED in 1.92.X IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyphForSize(ImFont* font, float font_size, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0)); // ADDED AND OBSOLETED in 1.92.X
#endif #endif
//unsigned int FontBuilderFlags; // OBSOLETED in 1.92.X: Renamed to FontLoaderFlags. //unsigned int FontBuilderFlags; // OBSOLETED in 1.92.X: Renamed to FontLoaderFlags.
//int TexDesiredWidth; // OBSOLETED in 1.92.X: Force texture width before calling Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height) //int TexDesiredWidth; // OBSOLETED in 1.92.X: Force texture width before calling Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height.
//typedef ImFontAtlasRect ImFontAtlasCustomRect; // OBSOLETED in 1.92.X //typedef ImFontAtlasRect ImFontAtlasCustomRect; // OBSOLETED in 1.92.X
//typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ //typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+
//typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+ //typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+
@@ -3958,7 +3959,7 @@ struct ImFont
IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** out_remaining = NULL); IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** out_remaining = NULL);
IMGUI_API const char* CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width); IMGUI_API const char* CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width);
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 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, bool cpu_fine_clip = false); 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 #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); }
#endif #endif

View File

@@ -3312,7 +3312,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d
ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX); ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f);
//ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacingY, 0.0f); //ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f);
} }
ImGuiListClipper clipper; ImGuiListClipper clipper;
@@ -5265,7 +5265,7 @@ static void DemoWindowPopups()
// Typical use for regular windows: // Typical use for regular windows:
// bool my_tool_is_active = false; if (ImGui::Button("Open")) my_tool_is_active = true; [...] if (my_tool_is_active) Begin("My Tool", &my_tool_is_active) { [...] } End(); // bool my_tool_is_active = false; if (ImGui::Button("Open")) my_tool_is_active = true; [...] if (my_tool_is_active) Begin("My Tool", &my_tool_is_active) { [...] } End();
// Typical use for popups: // Typical use for popups:
// if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup") { [...] EndPopup(); } // if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup")) { [...] EndPopup(); }
// With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state. // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state.
// This may be a bit confusing at first but it should quickly make sense. Follow on the examples below. // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below.
@@ -5851,7 +5851,7 @@ static void DemoWindowTables()
ImGui::SameLine(); ImGui::RadioButton("Text", &contents_type, CT_Text); ImGui::SameLine(); ImGui::RadioButton("Text", &contents_type, CT_Text);
ImGui::SameLine(); ImGui::RadioButton("FillButton", &contents_type, CT_FillButton); ImGui::SameLine(); ImGui::RadioButton("FillButton", &contents_type, CT_FillButton);
ImGui::Checkbox("Display headers", &display_headers); ImGui::Checkbox("Display headers", &display_headers);
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers"); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)");
PopStyleCompact(); PopStyleCompact();
if (ImGui::BeginTable("table1", 3, flags)) if (ImGui::BeginTable("table1", 3, flags))
@@ -7344,7 +7344,7 @@ static void DemoWindowTables()
ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH); ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH);
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH); ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH);
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH); ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH);
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers"); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)");
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)"); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)");
ImGui::TreePop(); ImGui::TreePop();
} }
@@ -9750,7 +9750,7 @@ static void ShowExampleAppConstrainedResize(bool* p_open)
IMGUI_DEMO_MARKER("Examples/Constrained Resizing window"); IMGUI_DEMO_MARKER("Examples/Constrained Resizing window");
if (ImGui::GetIO().KeyShift) if (ImGui::GetIO().KeyShift)
{ {
// Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture. // Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture)
ImVec2 avail_size = ImGui::GetContentRegionAvail(); ImVec2 avail_size = ImGui::GetContentRegionAvail();
ImVec2 pos = ImGui::GetCursorScreenPos(); ImVec2 pos = ImGui::GetCursorScreenPos();
ImGui::ColorButton("viewport", ImVec4(0.5f, 0.2f, 0.5f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, avail_size); ImGui::ColorButton("viewport", ImVec4(0.5f, 0.2f, 0.5f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, avail_size);

View File

@@ -48,7 +48,7 @@ Index of this file:
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). #pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer) #pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
#endif #endif
// Clang/GCC warnings with -Weverything // Clang/GCC warnings with -Weverything
@@ -1724,7 +1724,7 @@ void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32
clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z); clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z);
clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w); clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w);
} }
font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip_rect != NULL); font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, (cpu_fine_clip_rect != NULL) ? ImDrawTextFlags_CpuFineClip : ImDrawTextFlags_None);
} }
void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end) void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end)
@@ -5346,10 +5346,11 @@ ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float fo
} }
// Trim trailing space and find beginning of next line // Trim trailing space and find beginning of next line
static inline const char* CalcWordWrapNextLineStartA(const char* text, const char* text_end) const char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_end, ImDrawTextFlags flags)
{ {
while (text < text_end && ImCharIsBlankA(*text)) if ((flags & ImDrawTextFlags_WrapKeepBlanks) == 0)
text++; while (text < text_end && ImCharIsBlankA(*text))
text++;
if (*text == '\n') if (*text == '\n')
text++; text++;
return text; return text;
@@ -5358,7 +5359,7 @@ static inline const char* CalcWordWrapNextLineStartA(const char* text, const cha
// Simple word-wrapping for English, not full-featured. Please submit failing cases! // Simple word-wrapping for English, not full-featured. Please submit failing cases!
// This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end. // This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.) // FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
const char* ImFont::CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width) const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags)
{ {
// For references, possible wrap point marked with ^ // For references, possible wrap point marked with ^
// "aaa bbb, ccc,ddd. eee fff. ggg!" // "aaa bbb, ccc,ddd. eee fff. ggg!"
@@ -5372,7 +5373,7 @@ const char* ImFont::CalcWordWrapPosition(float size, const char* text, const cha
// Cut words that cannot possibly fit within one line. // Cut words that cannot possibly fit within one line.
// e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish" // e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
ImFontBaked* baked = GetFontBaked(size); ImFontBaked* baked = font->GetFontBaked(size);
const float scale = size / baked->Size; const float scale = size / baked->Size;
float line_width = 0.0f; float line_width = 0.0f;
@@ -5398,12 +5399,7 @@ const char* ImFont::CalcWordWrapPosition(float size, const char* text, const cha
if (c < 32) if (c < 32)
{ {
if (c == '\n') if (c == '\n')
{ return s; // Direct return, skip "Wrap_width is too small to fit anything" path.
line_width = word_width = blank_width = 0.0f;
inside_word = true;
s = next_s;
continue;
}
if (c == '\r') if (c == '\r')
{ {
s = next_s; s = next_s;
@@ -5438,6 +5434,8 @@ const char* ImFont::CalcWordWrapPosition(float size, const char* text, const cha
{ {
prev_word_end = word_end; prev_word_end = word_end;
line_width += word_width + blank_width; line_width += word_width + blank_width;
if ((flags & ImDrawTextFlags_WrapKeepBlanks) && line_width <= wrap_width)
prev_word_end = s;
word_width = blank_width = 0.0f; word_width = blank_width = 0.0f;
} }
@@ -5464,14 +5462,21 @@ const char* ImFont::CalcWordWrapPosition(float size, const char* text, const cha
return s; return s;
} }
ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** out_remaining) const char* ImFont::CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width)
{
return ImFontCalcWordWrapPositionEx(this, size, text, text_end, wrap_width, ImDrawTextFlags_None);
}
ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags)
{ {
if (!text_end) if (!text_end)
text_end = text_begin + ImStrlen(text_begin); // FIXME-OPT: Need to avoid this. text_end = text_begin + ImStrlen(text_begin); // FIXME-OPT: Need to avoid this.
if (!text_end_display)
text_end_display = text_end;
ImFontBaked* baked = font->GetFontBaked(size);
const float line_height = size; const float line_height = size;
ImFontBaked* baked = GetFontBaked(size); const float scale = line_height / baked->Size;
const float scale = size / baked->Size;
ImVec2 text_size = ImVec2(0, 0); ImVec2 text_size = ImVec2(0, 0);
float line_width = 0.0f; float line_width = 0.0f;
@@ -5480,13 +5485,14 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
const char* word_wrap_eol = NULL; const char* word_wrap_eol = NULL;
const char* s = text_begin; const char* s = text_begin;
while (s < text_end) while (s < text_end_display)
{ {
// Word-wrapping
if (word_wrap_enabled) if (word_wrap_enabled)
{ {
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if (!word_wrap_eol) if (!word_wrap_eol)
word_wrap_eol = CalcWordWrapPosition(size, s, text_end, wrap_width - line_width); word_wrap_eol = ImFontCalcWordWrapPositionEx(font, size, s, text_end, wrap_width - line_width, flags);
if (s >= word_wrap_eol) if (s >= word_wrap_eol)
{ {
@@ -5494,8 +5500,10 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
text_size.x = line_width; text_size.x = line_width;
text_size.y += line_height; text_size.y += line_height;
line_width = 0.0f; line_width = 0.0f;
s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); // Wrapping skips upcoming blanks
if (flags & ImDrawTextFlags_StopOnNewLine)
break;
word_wrap_eol = NULL; word_wrap_eol = NULL;
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
continue; continue;
} }
} }
@@ -5508,18 +5516,17 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
else else
s += ImTextCharFromUtf8(&c, s, text_end); s += ImTextCharFromUtf8(&c, s, text_end);
if (c < 32) if (c == '\n')
{ {
if (c == '\n') text_size.x = ImMax(text_size.x, line_width);
{ text_size.y += line_height;
text_size.x = ImMax(text_size.x, line_width); line_width = 0.0f;
text_size.y += line_height; if (flags & ImDrawTextFlags_StopOnNewLine)
line_width = 0.0f; break;
continue; continue;
}
if (c == '\r')
continue;
} }
if (c == '\r')
continue;
// Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);' // Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);'
float char_width = (c < (unsigned int)baked->IndexAdvanceX.Size) ? baked->IndexAdvanceX.Data[c] : -1.0f; float char_width = (c < (unsigned int)baked->IndexAdvanceX.Size) ? baked->IndexAdvanceX.Data[c] : -1.0f;
@@ -5539,7 +5546,10 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
if (text_size.x < line_width) if (text_size.x < line_width)
text_size.x = line_width; text_size.x = line_width;
if (line_width > 0 || text_size.y == 0.0f) if (out_offset != NULL)
*out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n
if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n
text_size.y += line_height; text_size.y += line_height;
if (out_remaining != NULL) if (out_remaining != NULL)
@@ -5548,6 +5558,11 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
return text_size; return text_size;
} }
ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** out_remaining)
{
return ImFontCalcTextSizeEx(this, size, max_width, wrap_width, text_begin, text_end, text_end, out_remaining, NULL, ImDrawTextFlags_None);
}
// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip) void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip)
{ {
@@ -5588,7 +5603,8 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im
} }
// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
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, bool cpu_fine_clip) // DO NOT CALL DIRECTLY THIS WILL CHANGE WIDLY IN 2025-2025. 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 // Align to be pixel perfect
begin: begin:
@@ -5618,8 +5634,8 @@ begin:
// FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPosition(). // FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPosition().
// If the specs for CalcWordWrapPosition() were reworked to optionally return on \n we could combine both. // If the specs for CalcWordWrapPosition() were reworked to optionally return on \n we could combine both.
// However it is still better than nothing performing the fast-forward! // However it is still better than nothing performing the fast-forward!
s = CalcWordWrapPosition(size, s, line_end ? line_end : text_end, wrap_width); s = ImFontCalcWordWrapPositionEx(this, size, s, line_end ? line_end : text_end, wrap_width, flags);
s = CalcWordWrapNextLineStartA(s, text_end); s = ImTextCalcWordWrapNextLineStart(s, text_end, flags);
} }
else else
{ {
@@ -5654,6 +5670,7 @@ begin:
ImDrawIdx* idx_write = draw_list->_IdxWritePtr; ImDrawIdx* idx_write = draw_list->_IdxWritePtr;
unsigned int vtx_index = draw_list->_VtxCurrentIdx; unsigned int vtx_index = draw_list->_VtxCurrentIdx;
const int cmd_count = draw_list->CmdBuffer.Size; const int cmd_count = draw_list->CmdBuffer.Size;
const bool cpu_fine_clip = (flags & ImDrawTextFlags_CpuFineClip) != 0;
const ImU32 col_untinted = col | ~IM_COL32_A_MASK; const ImU32 col_untinted = col | ~IM_COL32_A_MASK;
const char* word_wrap_eol = NULL; const char* word_wrap_eol = NULL;
@@ -5664,7 +5681,7 @@ begin:
{ {
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if (!word_wrap_eol) if (!word_wrap_eol)
word_wrap_eol = CalcWordWrapPosition(size, s, text_end, wrap_width - (x - origin_x)); word_wrap_eol = ImFontCalcWordWrapPositionEx(this, size, s, text_end, wrap_width - (x - origin_x), flags);
if (s >= word_wrap_eol) if (s >= word_wrap_eol)
{ {
@@ -5673,7 +5690,7 @@ begin:
if (y > clip_rect.w) if (y > clip_rect.w)
break; // break out of main loop break; // break out of main loop
word_wrap_eol = NULL; word_wrap_eol = NULL;
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); // Wrapping skips upcoming blanks
continue; continue;
} }
} }

View File

@@ -77,8 +77,8 @@ Index of this file:
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning (push) #pragma warning (push)
#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) #pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport)
#pragma warning (disable: 26812) // The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer)
#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). #pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
#endif #endif
@@ -199,6 +199,7 @@ typedef int ImGuiDataAuthority; // -> enum ImGuiDataAuthority_ // E
typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical
// Flags // Flags
typedef int ImDrawTextFlags; // -> enum ImDrawTextFlags_ // Flags: for ImTextCalcWordWrapPositionEx()
typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later)
typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags
typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow() typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow()
@@ -440,6 +441,18 @@ IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, cons
IMGUI_API const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr); // return previous UTF-8 code-point. IMGUI_API const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr); // return previous UTF-8 code-point.
IMGUI_API int ImTextCountLines(const char* in_text, const char* in_text_end); // return number of lines taken by text. trailing carriage return doesn't count as an extra line. IMGUI_API int ImTextCountLines(const char* in_text, const char* in_text_end); // return number of lines taken by text. trailing carriage return doesn't count as an extra line.
// Helpers: High-level text functions (DO NOT USE!!! THIS IS A MINIMAL SUBSET OF LARGER UPCOMING CHANGES)
enum ImDrawTextFlags_
{
ImDrawTextFlags_None = 0,
ImDrawTextFlags_CpuFineClip = 1 << 0, // Must be == 1/true for legacy with 'bool cpu_fine_clip' arg to RenderText()
ImDrawTextFlags_WrapKeepBlanks = 1 << 1,
ImDrawTextFlags_StopOnNewLine = 1 << 2,
};
IMGUI_API ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags);
IMGUI_API const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags = 0);
IMGUI_API const char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_end, ImDrawTextFlags flags = 0); // trim trailing space and find beginning of next line
// Helpers: File System // Helpers: File System
#ifdef IMGUI_DISABLE_FILE_FUNCTIONS #ifdef IMGUI_DISABLE_FILE_FUNCTIONS
#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS #define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
@@ -1013,6 +1026,15 @@ enum ImGuiHoveredFlagsPrivate_
// Extend ImGuiInputTextFlags_ // Extend ImGuiInputTextFlags_
enum ImGuiInputTextFlagsPrivate_ enum ImGuiInputTextFlagsPrivate_
{ {
// [Experimental]
// Word-wrapping caveats:
// - Not well tested yet. Please report any incorrect cursor movement, selection behavior etc. bug to https://github.com/ocornut/imgui/issues/3237.
// - With our current design it is _much_ slower than a regular text field. Editing a <50K buffer will generally be ok, but editing a 1MB buffer will waste meaningful amount of CPU.
// We are likely to not make the feature public until this is fixed (which requires bigger changes to InputText will be be generally desirable for this and other features)
// - Wrapping of long words/sections (e.g. words that are larger than available width) is currently visually not pleasing.
// - Vertical scrollbar is currently always visible.
ImGuiInputTextFlags_WordWrap = 1 << 24, // InputTextMultine(): wrap lines that are too long. (Ref #3237, #952, #1062)
// [Internal] // [Internal]
ImGuiInputTextFlags_Multiline = 1 << 26, // For internal use by InputTextMultiline() ImGuiInputTextFlags_Multiline = 1 << 26, // For internal use by InputTextMultiline()
ImGuiInputTextFlags_MergedItem = 1 << 27, // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match. ImGuiInputTextFlags_MergedItem = 1 << 27, // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match.
@@ -1232,11 +1254,15 @@ struct IMGUI_API ImGuiInputTextState
ImVector<char> CallbackTextBackup; // temporary storage for callback to support automatic reconcile of undo-stack ImVector<char> CallbackTextBackup; // temporary storage for callback to support automatic reconcile of undo-stack
int BufCapacity; // end-user buffer capacity (include zero terminator) int BufCapacity; // end-user buffer capacity (include zero terminator)
ImVec2 Scroll; // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y) ImVec2 Scroll; // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y)
int LineCount; // last line count (solely for debugging)
float WrapWidth; // word-wrapping width
float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately
bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!)
bool CursorCenterY; // set when we want scrolling to be centered over the cursor position (while resizing a word-wrapping field)
bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection
bool Edited; // edited this frame bool Edited; // edited this frame
bool WantReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version. bool WantReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version.
ImS8 LastMoveDirectionLR; // ImGuiDir_Left or ImGuiDir_Right. track last movement direction so when cursor cross over a word-wrapping boundaries we can display it on either line depending on last move.s
int ReloadSelectionStart; int ReloadSelectionStart;
int ReloadSelectionEnd; int ReloadSelectionEnd;
@@ -1246,6 +1272,7 @@ struct IMGUI_API ImGuiInputTextState
void ClearFreeMemory() { TextA.clear(); TextToRevertTo.clear(); } void ClearFreeMemory() { TextA.clear(); TextToRevertTo.clear(); }
void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation
void OnCharPressed(unsigned int c); void OnCharPressed(unsigned int c);
float GetPreferredOffsetX() const;
// Cursor & Selection // Cursor & Selection
void CursorAnimReset(); void CursorAnimReset();

View File

@@ -437,7 +437,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
if (table->InnerWindow->SkipItems && outer_window_is_measuring_size) if (table->InnerWindow->SkipItems && outer_window_is_measuring_size)
table->InnerWindow->SkipItems = false; table->InnerWindow->SkipItems = false;
// When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned) // When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned
if (instance_no == 0) if (instance_no == 0)
{ {
table->HasScrollbarYPrev = table->HasScrollbarYCurr; table->HasScrollbarYPrev = table->HasScrollbarYCurr;

View File

@@ -135,8 +135,8 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1);
// For InputTextEx() // For InputTextEx()
static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false); static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false);
static int InputTextCalcTextLenAndLineCount(ImGuiContext* ctx, const char* text_begin, const char** out_text_end); static int InputTextCalcTextLenAndLineCount(ImGuiContext* ctx, const char* text_begin, const char** out_text_end, float wrap_width);
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining = NULL, ImVec2* out_offset = NULL, ImDrawTextFlags flags = 0);
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// [SECTION] Widgets: Text, etc. // [SECTION] Widgets: Text, etc.
@@ -3917,9 +3917,6 @@ bool ImGui::InputDouble(const char* label, double* v, double step, double step_f
// - InputText() // - InputText()
// - InputTextWithHint() // - InputTextWithHint()
// - InputTextMultiline() // - InputTextMultiline()
// - InputTextGetCharInfo() [Internal]
// - InputTextReindexLines() [Internal]
// - InputTextReindexLinesRange() [Internal]
// - InputTextEx() [Internal] // - InputTextEx() [Internal]
// - DebugNodeInputTextState() [Internal] // - DebugNodeInputTextState() [Internal]
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
@@ -3947,10 +3944,11 @@ bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, si
} }
// This is only used in the path where the multiline widget is inactive. // This is only used in the path where the multiline widget is inactive.
static int InputTextCalcTextLenAndLineCount(ImGuiContext*, const char* text_begin, const char** out_text_end) static int InputTextCalcTextLenAndLineCount(ImGuiContext* ctx, const char* text_begin, const char** out_text_end, float wrap_width)
{ {
int line_count = 0; int line_count = 0;
const char* s = text_begin; const char* s = text_begin;
if (wrap_width == 0.0f)
{ {
while (true) while (true)
{ {
@@ -3964,59 +3962,32 @@ static int InputTextCalcTextLenAndLineCount(ImGuiContext*, const char* text_begi
s = s_eol + 1; s = s_eol + 1;
} }
} }
else
{
// FIXME-WORDWRAP, FIXME-OPT: This is very suboptimal.
// We basically want both text_end and text_size, they could more optimally be emitted from a RenderText call that uses word-wrapping.
ImGuiContext& g = *ctx;
ImFont* font = g.Font;
const char* text_end = text_begin + strlen(text_begin);
while (s < text_end)
{
s = ImFontCalcWordWrapPositionEx(font, g.FontSize, s, text_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks);
s = (*s == '\n') ? s + 1 : s;
line_count++;
}
if (text_end > text_begin && text_end[-1] == '\n')
line_count++;
IM_ASSERT(s == text_end);
}
*out_text_end = s; *out_text_end = s;
return line_count; return line_count;
} }
// FIXME: Ideally we'd share code with ImFont::CalcTextSizeA() static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags)
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining, ImVec2* out_offset, bool stop_on_new_line)
{ {
ImGuiContext& g = *ctx; ImGuiContext& g = *ctx;
//ImFont* font = g.Font; ImGuiInputTextState* obj = &g.InputTextState;
ImFontBaked* baked = g.FontBaked; return ImFontCalcTextSizeEx(g.Font, g.FontSize, FLT_MAX, obj->WrapWidth, text_begin, text_end_display, text_end, out_remaining, out_offset, flags);
const float line_height = g.FontSize;
const float scale = line_height / baked->Size;
ImVec2 text_size = ImVec2(0, 0);
float line_width = 0.0f;
const char* s = text_begin;
while (s < text_end)
{
unsigned int c = (unsigned int)*s;
if (c < 0x80)
s += 1;
else
s += ImTextCharFromUtf8(&c, s, text_end);
if (c == '\n')
{
text_size.x = ImMax(text_size.x, line_width);
text_size.y += line_height;
line_width = 0.0f;
if (stop_on_new_line)
break;
continue;
}
if (c == '\r')
continue;
line_width += baked->GetCharAdvance((ImWchar)c) * scale;
}
if (text_size.x < line_width)
text_size.x = line_width;
if (out_offset)
*out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n
if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n
text_size.y += line_height;
if (remaining)
*remaining = s;
return text_size;
} }
// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar) // Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar)
@@ -4034,7 +4005,7 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* ob
{ {
const char* text = obj->TextSrc; const char* text = obj->TextSrc;
const char* text_remaining = NULL; const char* text_remaining = NULL;
const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, &text_remaining, NULL, true); const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, text + obj->TextLen, &text_remaining, NULL, ImDrawTextFlags_StopOnNewLine | ImDrawTextFlags_WrapKeepBlanks);
r->x0 = 0.0f; r->x0 = 0.0f;
r->x1 = size.x; r->x1 = size.x;
r->baseline_y_delta = size.y; r->baseline_y_delta = size.y;
@@ -4136,6 +4107,75 @@ static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx)
#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h
#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
// Reimplementation of stb_textedit_move_line_start()/stb_textedit_move_line_end() which supports word-wrapping.
static int STB_TEXTEDIT_MOVELINESTART_IMPL(ImGuiInputTextState* obj, ImStb::STB_TexteditState* state, int cursor)
{
if (state->single_line)
return 0;
if (obj->WrapWidth > 0.0f)
{
ImGuiContext& g = *obj->Ctx;
const char* p_cursor = obj->TextSrc + cursor;
const char* p_bol = ImStrbol(p_cursor, obj->TextSrc);
const char* p = p_bol;
const char* text_end = obj->TextSrc + obj->TextLen; // End of line would be enough
while (p >= p_bol)
{
const char* p_eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, p, text_end, obj->WrapWidth, ImDrawTextFlags_WrapKeepBlanks);
if (p == p_cursor) // If we are already on a visible beginning-of-line, return real beginning-of-line (would be same as regular handler below)
return (int)(p_bol - obj->TextSrc);
if (p_eol == p_cursor && obj->TextA[cursor] != '\n' && obj->LastMoveDirectionLR == ImGuiDir_Left)
return (int)(p_bol - obj->TextSrc);
if (p_eol >= p_cursor)
return (int)(p - obj->TextSrc);
p = (*p_eol == '\n') ? p_eol + 1 : p_eol;
}
}
// Regular handler, same as stb_textedit_move_line_start()
while (cursor > 0)
{
int prev_cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, cursor);
if (STB_TEXTEDIT_GETCHAR(obj, prev_cursor) == STB_TEXTEDIT_NEWLINE)
break;
cursor = prev_cursor;
}
return cursor;
}
static int STB_TEXTEDIT_MOVELINEEND_IMPL(ImGuiInputTextState* obj, ImStb::STB_TexteditState* state, int cursor)
{
int n = STB_TEXTEDIT_STRINGLEN(obj);
if (state->single_line)
return n;
if (obj->WrapWidth > 0.0f)
{
ImGuiContext& g = *obj->Ctx;
const char* p_cursor = obj->TextSrc + cursor;
const char* p = ImStrbol(p_cursor, obj->TextSrc);
const char* text_end = obj->TextSrc + obj->TextLen; // End of line would be enough
while (p < text_end)
{
const char* p_eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, p, text_end, obj->WrapWidth, ImDrawTextFlags_WrapKeepBlanks);
cursor = (int)(p_eol - obj->TextSrc);
if (p_eol == p_cursor && obj->LastMoveDirectionLR != ImGuiDir_Left) // If we are already on a visible end-of-line, switch to regular handle
break;
if (p_eol > p_cursor)
return cursor;
p = (*p_eol == '\n') ? p_eol + 1 : p_eol;
}
}
// Regular handler, same as stb_textedit_move_line_end()
while (cursor < n && STB_TEXTEDIT_GETCHAR(obj, cursor) != STB_TEXTEDIT_NEWLINE)
cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, cursor);
return cursor;
}
#define STB_TEXTEDIT_MOVELINESTART STB_TEXTEDIT_MOVELINESTART_IMPL
#define STB_TEXTEDIT_MOVELINEEND STB_TEXTEDIT_MOVELINEEND_IMPL
static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n) static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
{ {
// Offset remaining text (+ copy zero terminator) // Offset remaining text (+ copy zero terminator)
@@ -4239,6 +4279,11 @@ void ImGuiInputTextState::OnKeyPressed(int key)
stb_textedit_key(this, Stb, key); stb_textedit_key(this, Stb, key);
CursorFollow = true; CursorFollow = true;
CursorAnimReset(); CursorAnimReset();
const int key_u = (key & ~STB_TEXTEDIT_K_SHIFT);
if (key_u == STB_TEXTEDIT_K_LEFT || key_u == STB_TEXTEDIT_K_LINESTART || key_u == STB_TEXTEDIT_K_TEXTSTART || key_u == STB_TEXTEDIT_K_BACKSPACE || key_u == STB_TEXTEDIT_K_WORDLEFT)
LastMoveDirectionLR = ImGuiDir_Left;
else if (key_u == STB_TEXTEDIT_K_RIGHT || key_u == STB_TEXTEDIT_K_LINEEND || key_u == STB_TEXTEDIT_K_TEXTEND || key_u == STB_TEXTEDIT_K_DELETE || key_u == STB_TEXTEDIT_K_WORDRIGHT)
LastMoveDirectionLR = ImGuiDir_Right;
} }
void ImGuiInputTextState::OnCharPressed(unsigned int c) void ImGuiInputTextState::OnCharPressed(unsigned int c)
@@ -4260,6 +4305,7 @@ void ImGuiInputTextState::ClearSelection() { Stb->select_start
int ImGuiInputTextState::GetCursorPos() const { return Stb->cursor; } int ImGuiInputTextState::GetCursorPos() const { return Stb->cursor; }
int ImGuiInputTextState::GetSelectionStart() const { return Stb->select_start; } int ImGuiInputTextState::GetSelectionStart() const { return Stb->select_start; }
int ImGuiInputTextState::GetSelectionEnd() const { return Stb->select_end; } int ImGuiInputTextState::GetSelectionEnd() const { return Stb->select_end; }
float ImGuiInputTextState::GetPreferredOffsetX() const { return Stb->has_preferred_x ? Stb->preferred_x : -1; }
void ImGuiInputTextState::SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = TextLen; Stb->has_preferred_x = 0; } void ImGuiInputTextState::SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = TextLen; Stb->has_preferred_x = 0; }
void ImGuiInputTextState::ReloadUserBufAndSelectAll() { WantReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; } void ImGuiInputTextState::ReloadUserBufAndSelectAll() { WantReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }
void ImGuiInputTextState::ReloadUserBufAndKeepSelection() { WantReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; } void ImGuiInputTextState::ReloadUserBufAndKeepSelection() { WantReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; }
@@ -4409,7 +4455,7 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, Im
if (c == '.' || c == ',') if (c == '.' || c == ',')
c = c_decimal_point; c = c_decimal_point;
// Full-width -> half-width conversion for numeric fields (https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block) // Full-width -> half-width conversion for numeric fields: https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block)
// While this is mostly convenient, this has the side-effect for uninformed users accidentally inputting full-width characters that they may // While this is mostly convenient, this has the side-effect for uninformed users accidentally inputting full-width characters that they may
// scratch their head as to why it works in numerical fields vs in generic text fields it would require support in the font. // scratch their head as to why it works in numerical fields vs in generic text fields it would require support in the font.
if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_CharsHexadecimal)) if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_CharsHexadecimal))
@@ -4531,7 +4577,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
IM_ASSERT(buf != NULL && buf_size >= 0); IM_ASSERT(buf != NULL && buf_size >= 0);
IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys)
IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
IM_ASSERT(!((flags & ImGuiInputTextFlags_ElideLeft) && (flags & ImGuiInputTextFlags_Multiline))); // Multiline will not work with left-trimming IM_ASSERT(!((flags & ImGuiInputTextFlags_ElideLeft) && (flags & ImGuiInputTextFlags_Multiline))); // Multiline does not not work with left-trimming
IM_ASSERT((flags & ImGuiInputTextFlags_WordWrap) == 0 || (flags & ImGuiInputTextFlags_Password) == 0); // WordWrap does not work with Password mode.
IM_ASSERT((flags & ImGuiInputTextFlags_WordWrap) == 0 || (flags & ImGuiInputTextFlags_Multiline) != 0); // WordWrap does not work in single-line mode.
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO; ImGuiIO& io = g.IO;
@@ -4579,7 +4627,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges
bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoMove;
if (flags & ImGuiInputTextFlags_WordWrap)
window_flags |= ImGuiWindowFlags_AlwaysVerticalScrollbar; // FIXME-WORDWRAP: Makes things much simpler. Otherwise requires more work to track cursor reliably and avoid one-frame glitch.
bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), ImGuiChildFlags_Borders, window_flags);
g.NavActivateId = backup_activate_id; g.NavActivateId = backup_activate_id;
PopStyleVar(3); PopStyleVar(3);
PopStyleColor(); PopStyleColor();
@@ -4619,6 +4670,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;
const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0; const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0;
const bool is_wordwrap = (flags & ImGuiInputTextFlags_WordWrap) != 0;
const float wrap_width = is_wordwrap ? GetContentRegionAvail().x : 0.0f;
if (is_resizable) if (is_resizable)
IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag!
@@ -4774,6 +4827,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (is_password && !is_displaying_hint) if (is_password && !is_displaying_hint)
PushPasswordFont(); PushPasswordFont();
// Word-wrapping: attempt to keep cursor in view while resizing frame/parent
// FIXME-WORDWRAP: It would be better to preserve same relative offset.
if (is_wordwrap && state != NULL && state->ID == id && state->WrapWidth != wrap_width)
{
state->CursorCenterY = true;
state->WrapWidth = wrap_width;
render_cursor = true;
}
// Process mouse inputs and character inputs // Process mouse inputs and character inputs
if (g.ActiveId == id) if (g.ActiveId == id)
{ {
@@ -4781,6 +4843,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
state->Edited = false; state->Edited = false;
state->BufCapacity = buf_size; state->BufCapacity = buf_size;
state->Flags = flags; state->Flags = flags;
state->WrapWidth = wrap_width;
// Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
// Down the line we should have a cleaner library-wide concept of Selected vs Active. // Down the line we should have a cleaner library-wide concept of Selected vs Active.
@@ -4818,9 +4881,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
{ {
// Triple-click: Select line // Triple-click: Select line
const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor) == '\n'; const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor) == '\n';
state->WrapWidth = 0.0f; // Temporarily disable wrapping so we use real line start.
state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART); state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART);
state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT); state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT);
state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT); state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT);
state->WrapWidth = wrap_width;
if (!is_eol && is_multiline) if (!is_eol && is_multiline)
{ {
ImSwap(state->Stb->select_start, state->Stb->select_end); ImSwap(state->Stb->select_start, state->Stb->select_end);
@@ -5238,9 +5303,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
} }
const ImRect clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y); // Not using frame_bb.Max because we have adjusted size
ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
ImVec2 text_size(0.0f, 0.0f); ImVec2 text_size(0.0f, 0.0f);
ImRect clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y); // Not using frame_bb.Max because we have adjusted size
if (is_multiline)
clip_rect.ClipWith(draw_window->ClipRect);
// Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line
// without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether. // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether.
@@ -5283,7 +5350,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
const char* text_begin = buf_display; const char* text_begin = buf_display;
const char* text_end = text_begin + state->TextLen; const char* text_end = text_begin + state->TextLen;
ImVec2 cursor_offset, select_start_offset; ImVec2 cursor_offset;
float select_start_offset_y = 0.0f; // Offset of beginning of non-wrapped line for selection.
{ {
// Find lines numbers straddling cursor and selection min position // Find lines numbers straddling cursor and selection min position
@@ -5291,12 +5359,16 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
int selmin_line_no = render_selection ? -1 : -1000; int selmin_line_no = render_selection ? -1 : -1000;
const char* cursor_ptr = render_cursor ? text_begin + state->Stb->cursor : NULL; const char* cursor_ptr = render_cursor ? text_begin + state->Stb->cursor : NULL;
const char* selmin_ptr = render_selection ? text_begin + ImMin(state->Stb->select_start, state->Stb->select_end) : NULL; const char* selmin_ptr = render_selection ? text_begin + ImMin(state->Stb->select_start, state->Stb->select_end) : NULL;
const char* cursor_line_start = NULL;
const char* selmin_line_start = NULL;
bool cursor_straddle_word_wrap = false;
// Count lines and find line number for cursor and selection ends // Count lines and find line number for cursor and selection ends
// FIXME: Switch to zero-based index to reduce confusion. // FIXME: Switch to zero-based index to reduce confusion.
int line_count = 1; int line_count = 1;
if (is_multiline) if (is_multiline)
{ {
if (!is_wordwrap)
{ {
for (const char* s = text_begin; (s = (const char*)ImMemchr(s, '\n', (size_t)(text_end - s))) != NULL; s++) for (const char* s = text_begin; (s = (const char*)ImMemchr(s, '\n', (size_t)(text_end - s))) != NULL; s++)
{ {
@@ -5305,27 +5377,59 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
line_count++; line_count++;
} }
} }
else
{
bool is_start_of_non_wrapped_line = true;
int line_count_for_non_wrapped_line = 1;
for (const char* s = text_begin; s < text_end; s = (*s == '\n') ? s + 1 : s)
{
const char* s_eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, s, text_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks);
const char* s_prev = s;
s = s_eol;
if (cursor_line_no == -1 && s >= cursor_ptr) { cursor_line_start = s_prev; cursor_line_no = line_count; }
if (selmin_line_no == -1 && s >= selmin_ptr) { selmin_line_start = s_prev; selmin_line_no = line_count_for_non_wrapped_line; }
if (s == cursor_ptr && *cursor_ptr != '\n' && *cursor_ptr != 0)
cursor_straddle_word_wrap = true;
is_start_of_non_wrapped_line = (*s == '\n');
line_count++;
if (is_start_of_non_wrapped_line)
line_count_for_non_wrapped_line = line_count;
}
}
//IMGUI_DEBUG_LOG("%d\n", selmin_line_no);
} }
if (cursor_line_no == -1) if (cursor_line_no == -1)
cursor_line_no = line_count; cursor_line_no = line_count;
if (cursor_line_start == NULL)
cursor_line_start = ImStrbol(cursor_ptr, text_begin);
if (selmin_line_no == -1) if (selmin_line_no == -1)
selmin_line_no = line_count; selmin_line_no = line_count;
if (selmin_line_start == NULL)
selmin_line_start = ImStrbol(cursor_ptr, text_begin);
// Calculate 2d position by finding the beginning of the line and measuring distance // Calculate 2d position by finding the beginning of the line and measuring distance
cursor_offset.x = InputTextCalcTextSize(&g, ImStrbol(cursor_ptr, text_begin), cursor_ptr).x; if (render_cursor)
cursor_offset.y = cursor_line_no * g.FontSize;
if (selmin_line_no >= 0)
{ {
select_start_offset.x = InputTextCalcTextSize(&g, ImStrbol(selmin_ptr, text_begin), selmin_ptr).x; cursor_offset.x = InputTextCalcTextSize(&g, cursor_line_start, cursor_ptr, text_end, NULL, NULL, ImDrawTextFlags_WrapKeepBlanks).x;
select_start_offset.y = selmin_line_no * g.FontSize; cursor_offset.y = cursor_line_no * g.FontSize;
if (is_multiline && cursor_straddle_word_wrap && state->LastMoveDirectionLR == ImGuiDir_Left)
cursor_offset = ImVec2(0.0f, cursor_offset.y + g.FontSize);
} }
if (selmin_line_no >= 0)
select_start_offset_y = selmin_line_no * g.FontSize;
// Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224) // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224)
if (is_multiline) if (is_multiline)
{
if (is_wordwrap && text_end > text_begin && text_end[-1] != '\n')
line_count--;
text_size = ImVec2(inner_size.x, line_count * g.FontSize); text_size = ImVec2(inner_size.x, line_count * g.FontSize);
}
state->LineCount = line_count;
} }
// Scroll // Scroll
float new_scroll_y = scroll_y;
if (render_cursor && state->CursorFollow) if (render_cursor && state->CursorFollow)
{ {
// Horizontal scroll in chunks of quarter width // Horizontal scroll in chunks of quarter width
@@ -5348,17 +5452,26 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
{ {
// Test if cursor is vertically visible // Test if cursor is vertically visible
if (cursor_offset.y - g.FontSize < scroll_y) if (cursor_offset.y - g.FontSize < scroll_y)
scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); new_scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y) else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y)
scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f; new_scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f;
const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f);
scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y);
draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag
draw_window->Scroll.y = scroll_y;
} }
state->CursorFollow = false; state->CursorFollow = false;
} }
if (state->CursorCenterY)
{
if (is_multiline)
new_scroll_y = cursor_offset.y - g.FontSize - (inner_size.y * 0.5f - style.FramePadding.y);
state->CursorCenterY = false;
render_cursor = false;
}
if (new_scroll_y != scroll_y)
{
const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f);
scroll_y = ImClamp(new_scroll_y, 0.0f, scroll_max_y);
draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag
draw_window->Scroll.y = scroll_y;
}
// Draw selection // Draw selection
const ImVec2 draw_scroll = ImVec2(state->Scroll.x, 0.0f); const ImVec2 draw_scroll = ImVec2(state->Scroll.x, 0.0f);
@@ -5370,28 +5483,33 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests. ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests.
float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection.
float bg_offy_dn = is_multiline ? 0.0f : 2.0f; float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
ImVec2 rect_pos = draw_pos + select_start_offset - draw_scroll; float bg_min_width = IM_TRUNC(g.FontBaked->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
for (const char* p = text_selected_begin; p < text_selected_end; ) ImVec2 rect_pos = draw_pos - draw_scroll;
rect_pos.y += select_start_offset_y;
for (const char* p = ImStrbol(text_selected_begin, text_begin); p < text_selected_end; rect_pos.y += g.FontSize)
{ {
if (rect_pos.y > clip_rect.Max.y + g.FontSize) if (rect_pos.y > clip_rect.Max.y + g.FontSize)
break; break;
if (rect_pos.y < clip_rect.Min.y) const char* p_eol = is_wordwrap ? ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, p, text_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks) : (const char*)ImMemchr((void*)p, '\n', text_selected_end - p);
if (p_eol == NULL)
p_eol = text_selected_end;
const char* p_next = is_wordwrap ? (*p_eol == '\n' ? p_eol + 1 : p_eol) : (p_eol + 1);
if (rect_pos.y >= clip_rect.Min.y)
{ {
p = (const char*)ImMemchr((void*)p, '\n', text_selected_end - p); const char* line_selected_begin = (text_selected_begin > p) ? text_selected_begin : p;
p = p ? p + 1 : text_selected_end; const char* line_selected_end = (text_selected_end < p_eol) ? text_selected_end : p_eol;
if ((*p_eol == '\n' && text_selected_begin <= p_eol) || (text_selected_begin < p_eol))
{
ImVec2 rect_offset = CalcTextSize(p, line_selected_begin);
ImVec2 rect_size = CalcTextSize(line_selected_begin, line_selected_end);
rect_size.x = ImMax(rect_size.x, bg_min_width); // So we can see selected empty lines
ImRect rect(rect_pos + ImVec2(rect_offset.x, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_offset.x + rect_size.x, bg_offy_dn));
rect.ClipWith(clip_rect);
if (rect.Overlaps(clip_rect))
draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
}
} }
else p = p_next;
{
ImVec2 rect_size = InputTextCalcTextSize(&g, p, text_selected_end, &p, NULL, true);
if (rect_size.x <= 0.0f)
rect_size.x = IM_TRUNC(g.FontBaked->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn));
rect.ClipWith(clip_rect);
if (rect.Overlaps(clip_rect))
draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
rect_pos.x = draw_pos.x - draw_scroll.x;
}
rect_pos.y += g.FontSize;
} }
} }
@@ -5400,7 +5518,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
{ {
ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect.AsVec4()); if (col & IM_COL32_A_MASK)
g.Font->RenderText(draw_window->DrawList, g.FontSize, draw_pos - draw_scroll, col, clip_rect.AsVec4(), buf_display, buf_display_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks);
//draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, wrap_width, is_multiline ? NULL : &clip_rect.AsVec4());
} }
// Draw blinking cursor // Draw blinking cursor
@@ -5431,7 +5551,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
{ {
// Render text only (no selection, no cursor) // Render text only (no selection, no cursor)
if (is_multiline) if (is_multiline)
text_size = ImVec2(inner_size.x, InputTextCalcTextLenAndLineCount(&g, buf_display, &buf_display_end) * g.FontSize); // We don't need width text_size = ImVec2(inner_size.x, InputTextCalcTextLenAndLineCount(&g, buf_display, &buf_display_end, wrap_width) * g.FontSize); // We don't need width
else if (!is_displaying_hint && g.ActiveId == id) else if (!is_displaying_hint && g.ActiveId == id)
buf_display_end = buf_display + state->TextLen; buf_display_end = buf_display + state->TextLen;
else if (!is_displaying_hint) else if (!is_displaying_hint)
@@ -5445,7 +5565,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
const ImVec2 draw_scroll = /*state ? ImVec2(state->Scroll.x, 0.0f) :*/ ImVec2(0.0f, 0.0f); // Preserve scroll when inactive? const ImVec2 draw_scroll = /*state ? ImVec2(state->Scroll.x, 0.0f) :*/ ImVec2(0.0f, 0.0f); // Preserve scroll when inactive?
ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect.AsVec4()); if (col & IM_COL32_A_MASK)
g.Font->RenderText(draw_window->DrawList, g.FontSize, draw_pos - draw_scroll, col, clip_rect.AsVec4(), buf_display, buf_display_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks);
//draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, wrap_width, is_multiline ? NULL : &clip_rect.AsVec4());
} }
} }
@@ -5501,8 +5623,10 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
ImStb::StbUndoState* undo_state = &stb_state->undostate; ImStb::StbUndoState* undo_state = &stb_state->undostate;
Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId); Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId);
DebugLocateItemOnHover(state->ID); DebugLocateItemOnHover(state->ID);
Text("CurLenA: %d, Cursor: %d, Selection: %d..%d", state->TextLen, stb_state->cursor, stb_state->select_start, stb_state->select_end); Text("TextLen: %d, Cursor: %d%s, Selection: %d..%d", state->TextLen, stb_state->cursor,
Text("BufCapacity: %d", state->BufCapacity); (state->Flags & ImGuiInputTextFlags_WordWrap) ? (state->LastMoveDirectionLR == ImGuiDir_Left ? " (L)" : " (R)") : "",
stb_state->select_start, stb_state->select_end);
Text("BufCapacity: %d, LineCount: %d", state->BufCapacity, state->LineCount);
Text("(Internal Buffer: TextA Size: %d, Capacity: %d)", state->TextA.Size, state->TextA.Capacity); Text("(Internal Buffer: TextA Size: %d, Capacity: %d)", state->TextA.Size, state->TextA.Capacity);
Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x); Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x);
Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point); Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);
@@ -8376,7 +8500,7 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)
// - Optimized select can append unsorted, then sort in a second pass. Optimized unselect can clear in-place then compact in a second pass. // - Optimized select can append unsorted, then sort in a second pass. Optimized unselect can clear in-place then compact in a second pass.
// - A more optimal version wouldn't even use ImGuiStorage but directly a ImVector<ImGuiID> to reduce bandwidth, but this is a reasonable trade off to reuse code. // - A more optimal version wouldn't even use ImGuiStorage but directly a ImVector<ImGuiID> to reduce bandwidth, but this is a reasonable trade off to reuse code.
// - There are many ways this could be better optimized. The worse case scenario being: using BoxSelect2d in a grid, box-select scrolling down while wiggling // - There are many ways this could be better optimized. The worse case scenario being: using BoxSelect2d in a grid, box-select scrolling down while wiggling
// left and right: it affects coarse clipping + can emit multiple SetRange with 1 item each.) // left and right: it affects coarse clipping + can emit multiple SetRange with 1 item each.
// FIXME-OPT: For each block of consecutive SetRange request: // FIXME-OPT: For each block of consecutive SetRange request:
// - add all requests to a sorted list, store ID, selected, offset in ImGuiStorage. // - add all requests to a sorted list, store ID, selected, offset in ImGuiStorage.
// - rewrite sorted storage a single time. // - rewrite sorted storage a single time.
@@ -9096,7 +9220,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
{ {
// Menu inside a regular/vertical menu // Menu inside a regular/vertical menu
// (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.)
popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;
float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);
@@ -9303,7 +9427,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
{ {
// Menu item inside a vertical menu // Menu item inside a vertical menu
// (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. // 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 icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;
float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, 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);

View File

@@ -427,7 +427,7 @@ typedef struct
// //
// traverse the layout to locate the nearest character to a display position // traverse the layout to locate the nearest character to a display position
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y) static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y, int* out_side_on_line)
{ {
StbTexteditRow r; StbTexteditRow r;
int n = STB_TEXTEDIT_STRINGLEN(str); int n = STB_TEXTEDIT_STRINGLEN(str);
@@ -437,6 +437,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
r.x0 = r.x1 = 0; r.x0 = r.x1 = 0;
r.ymin = r.ymax = 0; r.ymin = r.ymax = 0;
r.num_chars = 0; r.num_chars = 0;
*out_side_on_line = 0;
// search rows to find one that straddles 'y' // search rows to find one that straddles 'y'
while (i < n) { while (i < n) {
@@ -456,7 +457,10 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
// below all text, return 'after' last character // below all text, return 'after' last character
if (i >= n) if (i >= n)
{
*out_side_on_line = 1;
return n; return n;
}
// check if it's before the beginning of the line // check if it's before the beginning of the line
if (x < r.x0) if (x < r.x0)
@@ -469,6 +473,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) { for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
float w = STB_TEXTEDIT_GETWIDTH(str, i, k); float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
if (x < prev_x+w) { if (x < prev_x+w) {
*out_side_on_line = (k == 0) ? 0 : 1;
if (x < prev_x+w/2) if (x < prev_x+w/2)
return k+i; return k+i;
else else
@@ -480,6 +485,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
} }
// if the last character is a newline, return that. otherwise return 'after' the last character // if the last character is a newline, return that. otherwise return 'after' the last character
*out_side_on_line = 1;
if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE) if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE)
return i+r.num_chars-1; return i+r.num_chars-1;
else else
@@ -491,6 +497,7 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st
{ {
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
// goes off the top or bottom of the text // goes off the top or bottom of the text
int side_on_line;
if( state->single_line ) if( state->single_line )
{ {
StbTexteditRow r; StbTexteditRow r;
@@ -498,16 +505,18 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st
y = r.ymin; y = r.ymin;
} }
state->cursor = stb_text_locate_coord(str, x, y); state->cursor = stb_text_locate_coord(str, x, y, &side_on_line);
state->select_start = state->cursor; state->select_start = state->cursor;
state->select_end = state->cursor; state->select_end = state->cursor;
state->has_preferred_x = 0; state->has_preferred_x = 0;
str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
} }
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location // API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
{ {
int p = 0; int p = 0;
int side_on_line;
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
// goes off the top or bottom of the text // goes off the top or bottom of the text
@@ -521,8 +530,9 @@ static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *sta
if (state->select_start == state->select_end) if (state->select_start == state->select_end)
state->select_start = state->cursor; state->select_start = state->cursor;
p = stb_text_locate_coord(str, x, y); p = stb_text_locate_coord(str, x, y, &side_on_line);
state->cursor = state->select_end = p; state->cursor = state->select_end = p;
str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
} }
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
@@ -572,6 +582,8 @@ static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING
STB_TEXTEDIT_LAYOUTROW(&r, str, i); STB_TEXTEDIT_LAYOUTROW(&r, str, i);
if (n < i + r.num_chars) if (n < i + r.num_chars)
break; break;
if (str->LastMoveDirectionLR == ImGuiDir_Right && str->Stb->cursor == i + r.num_chars && STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] Wrapping point handling
break;
if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line
break; // [DEAR IMGUI] break; // [DEAR IMGUI]
prev_start = i; prev_start = i;
@@ -668,6 +680,35 @@ static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditSt
} }
} }
// [DEAR IMGUI] Extracted this function so we can more easily add support for word-wrapping.
#ifndef STB_TEXTEDIT_MOVELINESTART
static int stb_textedit_move_line_start(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
{
if (state->single_line)
return 0;
while (cursor > 0) {
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, cursor);
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
break;
cursor = prev;
}
return cursor;
}
#define STB_TEXTEDIT_MOVELINESTART stb_textedit_move_line_start
#endif
#ifndef STB_TEXTEDIT_MOVELINEEND
static int stb_textedit_move_line_end(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
{
int n = STB_TEXTEDIT_STRINGLEN(str);
if (state->single_line)
return n;
while (cursor < n && STB_TEXTEDIT_GETCHAR(str, cursor) != STB_TEXTEDIT_NEWLINE)
cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, cursor);
return cursor;
}
#define STB_TEXTEDIT_MOVELINEEND stb_textedit_move_line_end
#endif
#ifdef STB_TEXTEDIT_IS_SPACE #ifdef STB_TEXTEDIT_IS_SPACE
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx ) static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
{ {
@@ -943,6 +984,8 @@ retry:
} }
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
if (state->cursor == find.first_char + find.length)
str->LastMoveDirectionLR = ImGuiDir_Left;
state->has_preferred_x = 1; state->has_preferred_x = 1;
state->preferred_x = goal_x; state->preferred_x = goal_x;
@@ -1007,6 +1050,10 @@ retry:
} }
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
if (state->cursor == find.first_char)
str->LastMoveDirectionLR = ImGuiDir_Right;
else if (state->cursor == find.prev_first)
str->LastMoveDirectionLR = ImGuiDir_Left;
state->has_preferred_x = 1; state->has_preferred_x = 1;
state->preferred_x = goal_x; state->preferred_x = goal_x;
@@ -1024,7 +1071,7 @@ retry:
prev_scan = prev; prev_scan = prev;
} }
find.first_char = find.prev_first; find.first_char = find.prev_first;
find.prev_first = prev_scan; find.prev_first = STB_TEXTEDIT_MOVELINESTART(str, state, prev_scan);
} }
break; break;
} }
@@ -1098,14 +1145,7 @@ retry:
case STB_TEXTEDIT_K_LINESTART: case STB_TEXTEDIT_K_LINESTART:
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
stb_textedit_move_to_first(state); stb_textedit_move_to_first(state);
if (state->single_line) state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
state->cursor = 0;
else while (state->cursor > 0) {
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
break;
state->cursor = prev;
}
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;
@@ -1113,13 +1153,9 @@ retry:
case STB_TEXTEDIT_K_LINEEND2: case STB_TEXTEDIT_K_LINEEND2:
#endif #endif
case STB_TEXTEDIT_K_LINEEND: { case STB_TEXTEDIT_K_LINEEND: {
int n = STB_TEXTEDIT_STRINGLEN(str);
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
stb_textedit_move_to_first(state); stb_textedit_move_to_first(state);
if (state->single_line) state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
state->cursor = n;
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;
} }
@@ -1130,14 +1166,7 @@ retry:
case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
stb_textedit_prep_selection_at_cursor(state); stb_textedit_prep_selection_at_cursor(state);
if (state->single_line) state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
state->cursor = 0;
else while (state->cursor > 0) {
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
break;
state->cursor = prev;
}
state->select_end = state->cursor; state->select_end = state->cursor;
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;
@@ -1146,13 +1175,9 @@ retry:
case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:
#endif #endif
case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {
int n = STB_TEXTEDIT_STRINGLEN(str);
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
stb_textedit_prep_selection_at_cursor(state); stb_textedit_prep_selection_at_cursor(state);
if (state->single_line) state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
state->cursor = n;
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
state->select_end = state->cursor; state->select_end = state->cursor;
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;