1
0
Fork 0
mirror of https://github.com/ocornut/imgui.git synced 2026-01-14 00:34:18 +00:00

Merge branch 'master' into docking

# Conflicts:
#	backends/imgui_impl_vulkan.cpp
This commit is contained in:
ocornut 2024-12-18 17:02:08 +01:00
commit edef72d497
9 changed files with 187 additions and 139 deletions

View file

@ -28,6 +28,7 @@
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2024-12-11: Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for platforms not supporting VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222)
// 2024-11-27: Vulkan: Make user-provided descriptor pool optional. As a convenience, when setting init_info->DescriptorPoolSize the backend will create one itself. (#8172, #4867)
// 2024-10-07: Vulkan: Changed default texture sampler to Clamp instead of Repeat/Wrap.
// 2024-10-07: Vulkan: Expose selected render state in ImGui_ImplVulkan_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
@ -1475,6 +1476,10 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V
// Create Swapchain
{
VkSurfaceCapabilitiesKHR cap;
err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap);
check_vk_result(err);
VkSwapchainCreateInfoKHR info = {};
info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
info.surface = wd->Surface;
@ -1484,19 +1489,15 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V
info.imageArrayLayers = 1;
info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family
info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
info.preTransform = (cap.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : cap.currentTransform;
info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
info.presentMode = wd->PresentMode;
info.clipped = VK_TRUE;
info.oldSwapchain = old_swapchain;
VkSurfaceCapabilitiesKHR cap;
err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap);
check_vk_result(err);
if (info.minImageCount < cap.minImageCount)
info.minImageCount = cap.minImageCount;
else if (cap.maxImageCount != 0 && info.minImageCount > cap.maxImageCount)
info.minImageCount = cap.maxImageCount;
if (cap.currentExtent.width == 0xffffffff)
{
info.imageExtent.width = wd->Width = w;

View file

@ -35,10 +35,33 @@ HOW TO UPDATE?
and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users.
- Please report any issue!
-----------------------------------------------------------------------
VERSION 1.91.7 WIP (In Progress)
-----------------------------------------------------------------------
Breaking changes:
Other changes:
- InputText: Fixed a bug where character replacements performed from a callback
were not applied when pasting from clipbard. (#8229)
- InputText: Fixed issue when activating a ReadOnly field when the underlying
value is being modified. (#8242)
- Drags: Added ImGuiSliderFlags_NoSpeedTweaks flag to disable keyboard
modifiers altering the tweak speed. Useful if you want to alter tweak speed
yourself based on your own logic. (#8223)
- Nav: Fixed an issue where Alt key would clear current active item on
windows with the ImGuiWindowFlags_NoNavInputs flag. (#8231)
- Backends: Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for
platforms not supporting VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222) [@Zer0xFF]
-----------------------------------------------------------------------
VERSION 1.91.6 (Released 2024-12-11)
-----------------------------------------------------------------------
Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.6
Breaking changes:
- Backends: DX12: Changed ImGui_ImplDX12_Init() signature to take a

View file

@ -1,4 +1,4 @@
// dear imgui, v1.91.6
// dear imgui, v1.91.7 WIP
// (main code and documentation)
// Help:
@ -3999,6 +3999,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
ActiveIdPreviousFrameIsAlive = false;
ActiveIdPreviousFrameHasBeenEditedBefore = false;
ActiveIdPreviousFrameWindow = NULL;
memset(&ActiveIdValueOnActivation, 0, sizeof(ActiveIdValueOnActivation));
LastActiveId = 0;
LastActiveIdTimer = 0.0f;
@ -4456,7 +4457,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
// This could be written in a more general way (e.g associate a hook to ActiveId),
// but since this is currently quite an exception we'll leave it as is.
// One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveId()
// One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveID()
if (g.InputTextState.ID == g.ActiveId)
InputTextDeactivateHook(g.ActiveId);
}
@ -7880,9 +7881,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
{
IM_ASSERT(window->IDStack.Size == 1);
window->IDStack.Size = 0; // As window->IDStack[0] == window->ID here, make sure TestEngine doesn't erroneously see window as parent of itself.
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
IMGUI_TEST_ENGINE_ITEM_ADD(window->ID, window->Rect(), NULL);
IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0);
window->IDStack.Size = 1;
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
}
#endif
@ -8189,7 +8193,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// [Test Engine] Register title bar / tab with MoveId.
#ifdef IMGUI_ENABLE_TEST_ENGINE
if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
{
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.ID, g.LastItemData.Rect, &g.LastItemData);
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
}
#endif
}
else
@ -14328,15 +14336,16 @@ static void ImGui::NavUpdateWindowing()
// Keyboard: Press and Release ALT to toggle menu layer
const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt };
bool windowing_toggle_layer_start = false;
for (ImGuiKey windowing_toggle_key : windowing_toggle_keys)
if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner))
{
windowing_toggle_layer_start = true;
g.NavWindowingToggleLayer = true;
g.NavWindowingToggleKey = windowing_toggle_key;
g.NavInputSource = ImGuiInputSource_Keyboard;
break;
}
if (g.NavWindow != NULL && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
for (ImGuiKey windowing_toggle_key : windowing_toggle_keys)
if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner))
{
windowing_toggle_layer_start = true;
g.NavWindowingToggleLayer = true;
g.NavWindowingToggleKey = windowing_toggle_key;
g.NavInputSource = ImGuiInputSource_Keyboard;
break;
}
if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard)
{
// We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)

10
imgui.h
View file

@ -1,4 +1,4 @@
// dear imgui, v1.91.6
// dear imgui, v1.91.7 WIP
// (headers)
// Help:
@ -28,8 +28,8 @@
// Library Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
#define IMGUI_VERSION "1.91.6"
#define IMGUI_VERSION_NUM 19160
#define IMGUI_VERSION "1.91.7 WIP"
#define IMGUI_VERSION_NUM 19162
#define IMGUI_HAS_TABLE
#define IMGUI_HAS_VIEWPORT // Viewport WIP branch
#define IMGUI_HAS_DOCK // Docking WIP branch
@ -700,6 +700,7 @@ namespace ImGui
// Widgets: List Boxes
// - This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
// - If you don't need a label you can probably simply use BeginChild() with the ImGuiChildFlags_FrameStyle flag for the same result.
// - You can submit contents and manage your selection state however you want it, by creating e.g. Selectable() or any other items.
// - The simplified/old ListBox() api are helpers over BeginListBox()/EndListBox() which are kept available for convenience purpose. This is analoguous to how Combos are created.
// - Choose frame width: size.x > 0.0f: custom / size.x < 0.0f or -FLT_MIN: right-align / size.x = 0.0f (default): use current ItemWidth
@ -1864,6 +1865,7 @@ enum ImGuiSliderFlags_
ImGuiSliderFlags_WrapAround = 1 << 8, // Enable wrapping around from max to min and from min to max. Only supported by DragXXX() functions for now.
ImGuiSliderFlags_ClampOnInput = 1 << 9, // Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.
ImGuiSliderFlags_ClampZeroRange = 1 << 10, // Clamp even if min==max==0.0f. Otherwise due to legacy reason DragXXX functions don't clamp with those values. When your clamping limits are dynamic you almost always want to use it.
ImGuiSliderFlags_NoSpeedTweaks = 1 << 11, // Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic.
ImGuiSliderFlags_AlwaysClamp = ImGuiSliderFlags_ClampOnInput | ImGuiSliderFlags_ClampZeroRange,
ImGuiSliderFlags_InvalidMask_ = 0x7000000F, // [Internal] We treat using those bits as being potentially a 'float power' argument from the previous API that has got miscast to this enum, and will trigger an assert if needed.
};
@ -3359,7 +3361,7 @@ struct ImFontConfig
unsigned int FontBuilderFlags; // 0 // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure.
float RasterizerMultiply; // 1.0f // Linearly brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. This is a silly thing we may remove in the future.
float RasterizerDensity; // 1.0f // DPI scale for rasterization, not altering other font metrics: make it easy to swap between e.g. a 100% and a 400% fonts for a zooming display. IMPORTANT: If you increase this it is expected that you increase font scale accordingly, otherwise quality may look lowered.
ImWchar EllipsisChar; // -1 // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used.
ImWchar EllipsisChar; // 0 // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used.
// [Internal]
char Name[40]; // Name (strictly to ease debugging)

View file

@ -1,4 +1,4 @@
// dear imgui, v1.91.6
// dear imgui, v1.91.7 WIP
// (demo code)
// Help:
@ -2370,6 +2370,8 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data)
ImGui::SameLine(); HelpMarker("Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits).");
ImGui::CheckboxFlags("ImGuiSliderFlags_NoInput", &flags, ImGuiSliderFlags_NoInput);
ImGui::SameLine(); HelpMarker("Disable CTRL+Click or Enter key allowing to input text directly into the widget.");
ImGui::CheckboxFlags("ImGuiSliderFlags_NoSpeedTweaks", &flags, ImGuiSliderFlags_NoSpeedTweaks);
ImGui::SameLine(); HelpMarker("Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic.");
ImGui::CheckboxFlags("ImGuiSliderFlags_WrapAround", &flags, ImGuiSliderFlags_WrapAround);
ImGui::SameLine(); HelpMarker("Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions)");

View file

@ -1,4 +1,4 @@
// dear imgui, v1.91.6
// dear imgui, v1.91.7 WIP
// (drawing and font code)
/*
@ -2383,7 +2383,7 @@ ImFontConfig::ImFontConfig()
GlyphMaxAdvanceX = FLT_MAX;
RasterizerMultiply = 1.0f;
RasterizerDensity = 1.0f;
EllipsisChar = (ImWchar)-1;
EllipsisChar = 0;
}
//-----------------------------------------------------------------------------
@ -2570,9 +2570,6 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
// - We may support it better later and remove this rounding.
new_font_cfg.SizePixels = ImTrunc(new_font_cfg.SizePixels);
if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1)
new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar;
// Pointers to ConfigData and BuilderData are otherwise dangling
ImFontAtlasUpdateConfigDataPointers(this);
@ -3641,8 +3638,8 @@ ImFont::ImFont()
{
FontSize = 0.0f;
FallbackAdvanceX = 0.0f;
FallbackChar = (ImWchar)-1;
EllipsisChar = (ImWchar)-1;
FallbackChar = 0;
EllipsisChar = 0;
EllipsisWidth = EllipsisCharStep = 0.0f;
EllipsisCharCount = 0;
FallbackGlyph = NULL;
@ -3681,7 +3678,7 @@ static ImWchar FindFirstExistingGlyph(ImFont* font, const ImWchar* candidate_cha
for (int n = 0; n < candidate_chars_count; n++)
if (font->FindGlyphNoFallback(candidate_chars[n]) != NULL)
return candidate_chars[n];
return (ImWchar)-1;
return 0;
}
void ImFont::BuildLookupTable()
@ -3748,17 +3745,17 @@ void ImFont::BuildLookupTable()
// Setup Ellipsis character. It is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis).
// However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character.
// FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots.
const ImWchar ellipsis_chars[] = { (ImWchar)0x2026, (ImWchar)0x0085 };
const ImWchar ellipsis_chars[] = { ConfigData->EllipsisChar, (ImWchar)0x2026, (ImWchar)0x0085 };
const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E };
if (EllipsisChar == (ImWchar)-1)
if (EllipsisChar == 0)
EllipsisChar = FindFirstExistingGlyph(this, ellipsis_chars, IM_ARRAYSIZE(ellipsis_chars));
const ImWchar dot_char = FindFirstExistingGlyph(this, dots_chars, IM_ARRAYSIZE(dots_chars));
if (EllipsisChar != (ImWchar)-1)
if (EllipsisChar != 0)
{
EllipsisCharCount = 1;
EllipsisWidth = EllipsisCharStep = FindGlyph(EllipsisChar)->X1;
}
else if (dot_char != (ImWchar)-1)
else if (dot_char != 0)
{
const ImFontGlyph* glyph = FindGlyph(dot_char);
EllipsisChar = dot_char;

View file

@ -1,4 +1,4 @@
// dear imgui, v1.91.6
// dear imgui, v1.91.7 WIP
// (internal structures/api)
// You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
@ -1132,8 +1132,10 @@ struct IMGUI_API ImGuiInputTextState
{
ImGuiContext* Ctx; // parent UI context (needs to be set explicitly by parent).
ImStbTexteditState* Stb; // State for stb_textedit.h
ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set.
ImGuiID ID; // widget id owning the text state
int TextLen; // UTF-8 length of the string in TextA (in bytes)
const char* TextSrc; // == TextA.Data unless read-only, in which case == buf passed to InputText(). Field only set and valid _inside_ the call InputText() call.
ImVector<char> TextA; // main UTF8 buffer. TextA.Size is a buffer size! Should always be >= buf_size passed by user (and of course >= CurLenA + 1).
ImVector<char> TextToRevertTo; // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)
ImVector<char> CallbackTextBackup; // temporary storage for callback to support automatic reconcile of undo-stack
@ -1143,9 +1145,8 @@ struct IMGUI_API ImGuiInputTextState
bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!)
bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection
bool Edited; // edited this frame
ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set.
bool ReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version.
int ReloadSelectionStart; // POSITIONS ARE IN IMWCHAR units *NOT* UTF-8 this is why this is not exposed yet.
bool WantReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version.
int ReloadSelectionStart;
int ReloadSelectionEnd;
ImGuiInputTextState();
@ -2304,6 +2305,7 @@ struct ImGuiContext
bool ActiveIdPreviousFrameIsAlive;
bool ActiveIdPreviousFrameHasBeenEditedBefore;
ImGuiWindow* ActiveIdPreviousFrameWindow;
ImGuiDataTypeStorage ActiveIdValueOnActivation; // Backup of initial value at the time of activation. ONLY SET BY SPECIFIC WIDGETS: DragXXX and SliderXXX.
ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation.
float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation.

View file

@ -1,4 +1,4 @@
// dear imgui, v1.91.6
// dear imgui, v1.91.7 WIP
// (tables and columns code)
/*

View file

@ -1,4 +1,4 @@
// dear imgui, v1.91.6
// dear imgui, v1.91.7 WIP
// (widgets code)
/*
@ -2440,9 +2440,9 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const
if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR))
{
adjust_delta = g.IO.MouseDelta[axis];
if (g.IO.KeyAlt)
if (g.IO.KeyAlt && !(flags & ImGuiSliderFlags_NoSpeedTweaks))
adjust_delta *= 1.0f / 100.0f;
if (g.IO.KeyShift)
if (g.IO.KeyShift && !(flags & ImGuiSliderFlags_NoSpeedTweaks))
adjust_delta *= 10.0f;
}
else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
@ -2450,7 +2450,7 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const
const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0;
const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow);
const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast);
const float tweak_factor = tweak_slow ? 1.0f / 10.0f : tweak_fast ? 10.0f : 1.0f;
const float tweak_factor = (flags & ImGuiSliderFlags_NoSpeedTweaks) ? 1.0f : tweak_slow ? 1.0f / 10.0f : tweak_fast ? 10.0f : 1.0f;
adjust_delta = GetNavTweakPressedAmount(axis) * tweak_factor;
v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision));
}
@ -2638,6 +2638,10 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
temp_input_is_active = true;
}
// Store initial value (not used by main lib but available as a convenience but some mods e.g. to revert)
if (make_active)
memcpy(&g.ActiveIdValueOnActivation, p_data, DataTypeGetInfo(data_type)->Size);
if (make_active && !temp_input_is_active)
{
SetActiveID(id, window);
@ -3228,6 +3232,10 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
if ((clicked && g.IO.KeyCtrl) || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))
temp_input_is_active = true;
// Store initial value (not used by main lib but available as a convenience but some mods e.g. to revert)
if (make_active)
memcpy(&g.ActiveIdValueOnActivation, p_data, DataTypeGetInfo(data_type)->Size);
if (make_active && !temp_input_is_active)
{
SetActiveID(id, window);
@ -3936,12 +3944,12 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c
namespace ImStb
{
static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->TextLen; }
static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->TextLen); return obj->TextA[idx]; }
static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextA.Data + line_start_idx + char_idx, obj->TextA.Data + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->TextLen); return obj->TextSrc[idx]; }
static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextSrc + line_start_idx + char_idx, obj->TextSrc + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
static char STB_TEXTEDIT_NEWLINE = '\n';
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
{
const char* text = obj->TextA.Data;
const char* text = obj->TextSrc;
const char* text_remaining = NULL;
const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, &text_remaining, NULL, true);
r->x0 = 0.0f;
@ -3960,15 +3968,15 @@ static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int id
if (idx >= obj->TextLen)
return obj->TextLen + 1;
unsigned int c;
return idx + ImTextCharFromUtf8(&c, obj->TextA.Data + idx, obj->TextA.Data + obj->TextLen);
return idx + ImTextCharFromUtf8(&c, obj->TextSrc + idx, obj->TextSrc + obj->TextLen);
}
static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
{
if (idx <= 0)
return -1;
const char* p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, obj->TextA.Data + idx);
return (int)(p - obj->TextA.Data);
const char* p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, obj->TextSrc + idx);
return (int)(p - obj->TextSrc);
}
static bool ImCharIsSeparatorW(unsigned int c)
@ -3991,10 +3999,10 @@ static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx)
if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
return 0;
const char* curr_p = obj->TextA.Data + idx;
const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p);
unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextA.Data + obj->TextLen);
unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextA.Data + obj->TextLen);
const char* curr_p = obj->TextSrc + idx;
const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, curr_p);
unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextSrc + obj->TextLen);
unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextSrc + obj->TextLen);
bool prev_white = ImCharIsBlankW(prev_c);
bool prev_separ = ImCharIsSeparatorW(prev_c);
@ -4007,10 +4015,10 @@ static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)
if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
return 0;
const char* curr_p = obj->TextA.Data + idx;
const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p);
unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextA.Data + obj->TextLen);
unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextA.Data + obj->TextLen);
const char* curr_p = obj->TextSrc + idx;
const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, curr_p);
unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextSrc + obj->TextLen);
unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextSrc + obj->TextLen);
bool prev_white = ImCharIsBlankW(prev_c);
bool prev_separ = ImCharIsSeparatorW(prev_c);
@ -4047,16 +4055,13 @@ static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx)
static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
{
// Offset remaining text (+ copy zero terminator)
IM_ASSERT(obj->TextSrc == obj->TextA.Data);
char* dst = obj->TextA.Data + pos;
char* src = obj->TextA.Data + pos + n;
memmove(dst, src, obj->TextLen - n - pos + 1);
obj->Edited = true;
obj->TextLen -= n;
// Offset remaining text (FIXME-OPT: Use memmove)
const char* src = obj->TextA.Data + pos + n;
while (char c = *src++)
*dst++ = c;
*dst = '\0';
}
static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const char* new_text, int new_text_len)
@ -4069,11 +4074,13 @@ static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ch
return false;
// Grow internal buffer if needed
IM_ASSERT(obj->TextSrc == obj->TextA.Data);
if (new_text_len + text_len + 1 > obj->TextA.Size)
{
if (!is_resizable)
return false;
obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1);
obj->TextSrc = obj->TextA.Data;
}
char* text = obj->TextA.Data;
@ -4171,26 +4178,25 @@ int ImGuiInputTextState::GetCursorPos() const { return Stb->cursor
int ImGuiInputTextState::GetSelectionStart() const { return Stb->select_start; }
int ImGuiInputTextState::GetSelectionEnd() const { return Stb->select_end; }
void ImGuiInputTextState::SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = TextLen; Stb->has_preferred_x = 0; }
void ImGuiInputTextState::ReloadUserBufAndSelectAll() { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }
void ImGuiInputTextState::ReloadUserBufAndKeepSelection() { ReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; }
void ImGuiInputTextState::ReloadUserBufAndMoveToEnd() { ReloadUserBuf = true; ReloadSelectionStart = 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::ReloadUserBufAndMoveToEnd() { WantReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; }
ImGuiInputTextCallbackData::ImGuiInputTextCallbackData()
{
memset(this, 0, sizeof(*this));
}
// Public API to manipulate UTF-8 text
// We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
// Public API to manipulate UTF-8 text from within a callback.
// FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
// Historically they existed because STB_TEXTEDIT_INSERTCHARS() etc. worked on our ImWchar
// buffer, but nowadays they both work on UTF-8 data. Should aim to merge both.
void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count)
{
IM_ASSERT(pos + bytes_count <= BufTextLen);
char* dst = Buf + pos;
const char* src = Buf + pos + bytes_count;
while (char c = *src++)
*dst++ = c;
*dst = '\0';
memmove(dst, src, BufTextLen - bytes_count - pos + 1);
if (CursorPos >= pos + bytes_count)
CursorPos -= bytes_count;
@ -4215,13 +4221,13 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
if (!is_resizable)
return;
// Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the mildly similar code (until we remove the U16 buffer altogether!)
ImGuiContext& g = *Ctx;
ImGuiInputTextState* edit_state = &g.InputTextState;
IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID);
IM_ASSERT(Buf == edit_state->TextA.Data);
int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
edit_state->TextA.resize(new_buf_size + 1);
edit_state->TextSrc = edit_state->TextA.Data;
Buf = edit_state->TextA.Data;
BufSize = edit_state->BufCapacity = new_buf_size;
}
@ -4339,26 +4345,23 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, Im
return true;
}
// Find the shortest single replacement we can make to get the new text from the old text.
// Important: needs to be run before TextW is rewritten with the new characters because calling STB_TEXTEDIT_GETCHAR() at the end.
// Find the shortest single replacement we can make to get from old_buf to new_buf
// Note that this doesn't directly alter state->TextA, state->TextLen. They are expected to be made valid separately.
// FIXME: Ideally we should transition toward (1) making InsertChars()/DeleteChars() update undo-stack (2) discourage (and keep reconcile) or obsolete (and remove reconcile) accessing buffer directly.
static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* state, const char* new_buf_a, int new_length_a)
static void InputTextReconcileUndoState(ImGuiInputTextState* state, const char* old_buf, int old_length, const char* new_buf, int new_length)
{
const char* old_buf = state->CallbackTextBackup.Data;
const int old_length = state->CallbackTextBackup.Size - 1;
const int shorter_length = ImMin(old_length, new_length_a);
const int shorter_length = ImMin(old_length, new_length);
int first_diff;
for (first_diff = 0; first_diff < shorter_length; first_diff++)
if (old_buf[first_diff] != new_buf_a[first_diff])
if (old_buf[first_diff] != new_buf[first_diff])
break;
if (first_diff == old_length && first_diff == new_length_a)
if (first_diff == old_length && first_diff == new_length)
return;
int old_last_diff = old_length - 1;
int new_last_diff = new_length_a - 1;
int new_last_diff = new_length - 1;
for (; old_last_diff >= first_diff && new_last_diff >= first_diff; old_last_diff--, new_last_diff--)
if (old_buf[old_last_diff] != new_buf_a[new_last_diff])
if (old_buf[old_last_diff] != new_buf[new_last_diff])
break;
const int insert_len = new_last_diff - first_diff + 1;
@ -4510,64 +4513,65 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX;
const bool init_reload_from_user_buf = (state != NULL && state->ReloadUserBuf);
const bool init_reload_from_user_buf = (state != NULL && state->WantReloadUserBuf);
const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state.
const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav);
const bool init_state = (init_make_active || user_scroll_active);
if ((init_state && g.ActiveId != id) || init_changed_specs || init_reload_from_user_buf)
if (init_reload_from_user_buf)
{
int new_len = (int)strlen(buf);
state->WantReloadUserBuf = false;
InputTextReconcileUndoState(state, state->TextA.Data, state->TextLen, buf, new_len);
state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string.
state->TextLen = new_len;
memcpy(state->TextA.Data, buf, state->TextLen + 1);
state->Stb->select_start = state->ReloadSelectionStart;
state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd;
state->CursorClamp();
}
else if ((init_state && g.ActiveId != id) || init_changed_specs)
{
// Access state even if we don't own it yet.
state = &g.InputTextState;
state->CursorAnimReset();
state->ReloadUserBuf = false;
// Backup state of deactivating item so they'll have a chance to do a write to output buffer on the same frame they report IsItemDeactivatedAfterEdit (#4714)
InputTextDeactivateHook(state->ID);
// Take a copy of the initial buffer value.
// From the moment we focused we are normally ignoring the content of 'buf' (unless we are in read-only mode)
const int buf_len = (int)strlen(buf);
if (!init_reload_from_user_buf)
{
// Take a copy of the initial buffer value.
state->TextToRevertTo.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string.
memcpy(state->TextToRevertTo.Data, buf, buf_len + 1);
}
state->TextToRevertTo.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string.
memcpy(state->TextToRevertTo.Data, buf, buf_len + 1);
// Preserve cursor position and undo/redo stack if we come back to same widget
// FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate?
bool recycle_state = (state->ID == id && !init_changed_specs && !init_reload_from_user_buf);
if (recycle_state && (state->TextLen != buf_len || (strncmp(state->TextA.Data, buf, buf_len) != 0)))
bool recycle_state = (state->ID == id && !init_changed_specs);
if (recycle_state && (state->TextLen != buf_len || (state->TextA.Data == NULL || strncmp(state->TextA.Data, buf, buf_len) != 0)))
recycle_state = false;
// Start edition
state->ID = id;
state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string.
state->TextLen = (int)strlen(buf);
memcpy(state->TextA.Data, buf, state->TextLen + 1);
if (!is_readonly)
{
state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string.
memcpy(state->TextA.Data, buf, state->TextLen + 1);
}
// Find initial scroll position for right alignment
state->Scroll = ImVec2(0.0f, 0.0f);
if (flags & ImGuiInputTextFlags_ElideLeft)
state->Scroll.x += ImMax(0.0f, CalcTextSize(buf).x - frame_size.x + style.FramePadding.x * 2.0f);
// Recycle existing cursor/selection/undo stack but clamp position
// Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
if (recycle_state)
{
// Recycle existing cursor/selection/undo stack but clamp position
// Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
state->CursorClamp();
}
else
{
stb_textedit_initialize_state(state->Stb, !is_multiline);
}
if (init_reload_from_user_buf)
{
state->Stb->select_start = state->ReloadSelectionStart;
state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd;
state->CursorClamp();
}
else if (!is_multiline)
if (!is_multiline)
{
if (flags & ImGuiInputTextFlags_AutoSelectAll)
select_all = true;
@ -4617,7 +4621,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// Expose scroll in a manner that is agnostic to us using a child window
if (is_multiline && state != NULL)
state->Scroll.y = draw_window->Scroll.y;
// Read-only mode always ever read from source buffer. Refresh TextLen when active.
if (is_readonly && state != NULL)
state->TextLen = (int)strlen(buf);
//if (is_readonly && state != NULL)
// state->TextA.clear(); // Uncomment to facilitate debugging, but we otherwise prefer to keep/amortize th allocation.
}
if (state != NULL)
state->TextSrc = is_readonly ? buf : state->TextA.Data;
// We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function)
if (g.ActiveId == id && state == NULL)
@ -4882,13 +4894,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// Cut, Copy
if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
{
// SetClipboardText() only takes null terminated strings + state->TextSrc may point to read-only user buffer, so we need to make a copy.
const int ib = state->HasSelection() ? ImMin(state->Stb->select_start, state->Stb->select_end) : 0;
const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->TextLen;
char backup = state->TextA.Data[ie];
state->TextA.Data[ie] = 0; // A bit of a hack since SetClipboardText only takes null terminated strings
SetClipboardText(state->TextA.Data + ib);
state->TextA.Data[ie] = backup;
g.TempBuffer.reserve(ie - ib + 1);
memcpy(g.TempBuffer.Data, state->TextSrc, ie - ib);
g.TempBuffer.Data[ie] = 0;
SetClipboardText(g.TempBuffer.Data);
}
if (is_cut)
{
@ -4904,25 +4916,27 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
{
// Filter pasted buffer
const int clipboard_len = (int)strlen(clipboard);
char* clipboard_filtered = (char*)IM_ALLOC(clipboard_len + 1);
int clipboard_filtered_len = 0;
ImVector<char> clipboard_filtered;
clipboard_filtered.reserve(clipboard_len + 1);
for (const char* s = clipboard; *s != 0; )
{
unsigned int c;
int len = ImTextCharFromUtf8(&c, s, NULL);
s += len;
int in_len = ImTextCharFromUtf8(&c, s, NULL);
s += in_len;
if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, true))
continue;
memcpy(clipboard_filtered + clipboard_filtered_len, s - len, len);
clipboard_filtered_len += len;
char c_utf8[5];
ImTextCharToUtf8(c_utf8, c);
int out_len = (int)strlen(c_utf8);
clipboard_filtered.resize(clipboard_filtered.Size + out_len);
memcpy(clipboard_filtered.Data + clipboard_filtered.Size - out_len, c_utf8, out_len);
}
clipboard_filtered[clipboard_filtered_len] = 0;
if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation
if (clipboard_filtered.Size > 0) // If everything was filtered, ignore the pasting operation
{
stb_textedit_paste(state, state->Stb, clipboard_filtered, clipboard_filtered_len);
clipboard_filtered.push_back(0);
stb_textedit_paste(state, state->Stb, clipboard_filtered.Data, clipboard_filtered.Size - 1);
state->CursorFollow = true;
}
MemFree(clipboard_filtered);
}
}
@ -5015,10 +5029,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
callback_data.UserData = callback_user_data;
// FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925
state->CallbackTextBackup.resize(state->TextLen + 1);
memcpy(state->CallbackTextBackup.Data, state->TextA.Data, state->TextLen + 1);
char* callback_buf = is_readonly ? buf : state->TextA.Data;
IM_ASSERT(callback_buf == state->TextSrc);
state->CallbackTextBackup.resize(state->TextLen + 1);
memcpy(state->CallbackTextBackup.Data, callback_buf, state->TextLen + 1);
callback_data.EventKey = event_key;
callback_data.Buf = callback_buf;
callback_data.BufTextLen = state->TextLen;
@ -5045,7 +5060,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
{
// Callback may update buffer and thus set buf_dirty even in read-only mode.
IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ?
InputTextReconcileUndoState(state, state->CallbackTextBackup.Data, state->CallbackTextBackup.Size - 1, callback_data.Buf, callback_data.BufTextLen);
state->TextLen = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
state->CursorAnimReset();
}
@ -5053,9 +5068,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
}
// Will copy result string if modified
if (!is_readonly && strcmp(state->TextA.Data, buf) != 0)
if (!is_readonly && strcmp(state->TextSrc, buf) != 0)
{
apply_new_text = state->TextA.Data;
apply_new_text = state->TextSrc;
apply_new_text_length = state->TextLen;
value_changed = true;
}
@ -5149,7 +5164,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// - Measure text height (for scrollbar)
// We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
// 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 = state->TextA.Data;
const char* text_begin = buf_display;
const char* text_end = text_begin + state->TextLen;
ImVec2 cursor_offset, select_start_offset;
@ -5330,6 +5345,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
g.LastItemData.StatusFlags = item_data_backup.StatusFlags;
}
}
if (state)
state->TextSrc = NULL;
// Log as text
if (g.LogEnabled && (!is_password || is_displaying_hint))
@ -8213,15 +8230,10 @@ void ImGuiSelectionExternalStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)
//-------------------------------------------------------------------------
// This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
// This handle some subtleties with capturing info from the label, but for 99% uses it could essentially be rewritten as:
// if (ImGui::BeginChild("...", ImVec2(ImGui::CalcItemWidth(), ImGui::GetTextLineHeight() * 7.5f), ImGuiChildFlags_FrameStyle))
// { .... }
// ImGui::EndChild();
// ImGui::SameLine();
// ImGui::AlignTextToFramePadding();
// ImGui::Text("Label");
// This handle some subtleties with capturing info from the label.
// If you don't need a label you can pretty much directly use ImGui::BeginChild() with ImGuiChildFlags_FrameStyle.
// Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. "##empty"
// Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.25 * item_height).
// Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.5f * item_height).
bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)
{
ImGuiContext& g = *GImGui;