From 1ffa7a40ac02a9edbc04c31db574304dbc017dac Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 20 May 2025 17:31:58 +0200 Subject: [PATCH 01/11] TextLinkOpenURL(): added bool return value on click. (#8645, #8451, #7660) --- docs/CHANGELOG.txt | 1 + imgui.h | 2 +- imgui_widgets.cpp | 9 +++++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 48201acdf..4fbbeea92 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -97,6 +97,7 @@ Other changes: which mitigates edge case issues in multi-viewport scenarios where abnormally large windows (e.g. determined programmatically) can lead to renderer backend trying to create abnormally large framebuffers. +- TextLinkOpenURL(): added bool return value on click. (#8645, #8451, #7660) - Nav: fixed assertion when holding gamepad FaceLeft/West button to open CTRL+Tab windowing + pressing a keyboard key. (#8525) - Error Handling: added better error report and recovery for extraneous diff --git a/imgui.h b/imgui.h index 6794abede..f90e4dfc0 100644 --- a/imgui.h +++ b/imgui.h @@ -559,7 +559,7 @@ namespace ImGui IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-FLT_MIN, 0), const char* overlay = NULL); IMGUI_API void Bullet(); // draw a small circle + keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses IMGUI_API bool TextLink(const char* label); // hyperlink text button, return true when clicked - IMGUI_API void TextLinkOpenURL(const char* label, const char* url = NULL); // hyperlink text button, automatically open file/url when clicked + IMGUI_API bool TextLinkOpenURL(const char* label, const char* url = NULL); // hyperlink text button, automatically open file/url when clicked // Widgets: Images // - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f0108d12b..b99e6e5c0 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1529,14 +1529,14 @@ bool ImGui::TextLink(const char* label) return pressed; } -void ImGui::TextLinkOpenURL(const char* label, const char* url) +bool ImGui::TextLinkOpenURL(const char* label, const char* url) { ImGuiContext& g = *GImGui; if (url == NULL) url = label; - if (TextLink(label)) - if (g.PlatformIO.Platform_OpenInShellFn != NULL) - g.PlatformIO.Platform_OpenInShellFn(&g, url); + bool pressed = TextLink(label); + if (pressed && g.PlatformIO.Platform_OpenInShellFn != NULL) + g.PlatformIO.Platform_OpenInShellFn(&g, url); SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when we same as label if (BeginPopupContextItem()) { @@ -1544,6 +1544,7 @@ void ImGui::TextLinkOpenURL(const char* label, const char* url) SetClipboardText(url); EndPopup(); } + return pressed; } //------------------------------------------------------------------------- From 143924bbf3eb5aa548711dd2e5fbaf5822772cf0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 20 May 2025 17:53:17 +0200 Subject: [PATCH 02/11] Image(), ImageWithBg(): added extra comments. (#8131, #8238) --- imgui.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index f90e4dfc0..f4b692ab2 100644 --- a/imgui.h +++ b/imgui.h @@ -566,6 +566,7 @@ namespace ImGui // - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above. // - Image() pads adds style.ImageBorderSize on each side, ImageButton() adds style.FramePadding on each side. // - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified. + // - An obsolete version of Image(), before 1.91.9 (March 2025), had a 'tint_col' parameter which is now supported by the ImageWithBg() function. IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1)); IMGUI_API void ImageWithBg(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); IMGUI_API bool ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); @@ -3649,7 +3650,7 @@ struct ImGuiPlatformImeData namespace ImGui { // OBSOLETED in 1.91.9 (from February 2025) - IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col); // <-- border_col was removed in favor of ImGuiCol_ImageBorder. + IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col); // <-- 'border_col' was removed in favor of ImGuiCol_ImageBorder. If you use 'tint_col', use ImageWithBg() instead. // OBSOLETED in 1.91.0 (from July 2024) static inline void PushButtonRepeat(bool repeat) { PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); } static inline void PopButtonRepeat() { PopItemFlag(); } From 5f0acadf7db1b6d469f0771284e2e3f7818443ce Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 20 May 2025 18:06:12 +0200 Subject: [PATCH 03/11] RenderTextEllipsis() added breaking comments. --- docs/CHANGELOG.txt | 4 +++- imgui.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4fbbeea92..f6207965e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -51,7 +51,9 @@ Breaking changes: in 1.89.6 (June 2023). - ForceDisplayRangeByIndices() --> IncludeItemsByIndex() - Backends: SDL3: Fixed casing typo in function name: (#8509, #8163, #7998, #7988) [@puugz] - - Imgui_ImplSDLGPU3_PrepareDrawData() -> ImGui_ImplSDLGPU3_PrepareDrawData() + - Imgui_ImplSDLGPU3_PrepareDrawData() -> ImGui_ImplSDLGPU3_PrepareDrawData() +- Internals: RenderTextEllipsis() function removed the 'float clip_max_x' parameter directly + preceding 'float ellipsis_max_x'. Values were identical for a vast majority of users. (#8387) Other changes: diff --git a/imgui.cpp b/imgui.cpp index 6a4c37f41..16632d8da 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -435,6 +435,7 @@ CODE - 2025/05/15 (1.92.0) - Commented out PushAllowKeyboardFocus()/PopAllowKeyboardFocus() which was obsoleted in 1.89.4. Use PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop)/PopItemFlag() instead. (#3092) - 2025/05/15 (1.92.0) - Commented out ImGuiListClipper::ForceDisplayRangeByIndices() which was obsoleted in 1.89.6. Use ImGuiListClipper::IncludeItemsByIndex() instead. - 2025/03/05 (1.91.9) - BeginMenu(): Internals: reworked mangling of menu windows to use "###Menu_00" etc. instead of "##Menu_00", allowing them to also store the menu name before it. This shouldn't affect code unless directly accessing menu window from their mangled name. + - 2025/04/16 (1.91.9) - Internals: RenderTextEllipsis() function removed the 'float clip_max_x' parameter directly preceding 'float ellipsis_max_x'. Values were identical for a vast majority of users. (#8387) - 2025/02/27 (1.91.9) - Image(): removed 'tint_col' and 'border_col' parameter from Image() function. Added ImageWithBg() replacement. (#8131, #8238) - old: void Image (ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1), ImVec4 tint_col = (1,1,1,1), ImVec4 border_col = (0,0,0,0)); - new: void Image (ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1)); @@ -3696,6 +3697,7 @@ void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, cons // Another overly complex function until we reorganize everything into a nice all-in-one helper. // This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) from 'ellipsis_max_x' which may be beyond it. // This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move. +// (BREAKING) On 2025/04/16 we removed the 'float clip_max_x' parameters which was preceeding 'float ellipsis_max' and was the same value for 99% of users. void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known) { ImGuiContext& g = *GImGui; From 407a0b972eac6166095d2b5b5b0896bad6e9687a Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 24 Jan 2025 18:10:42 +0100 Subject: [PATCH 04/11] (Breaking) Fonts: CalcWordWrapPositionA() -> CalcWordWrapPosition(), takes size instead of scale. This will be needed for upcoming changes. --- docs/CHANGELOG.txt | 10 ++++++++-- imgui.cpp | 4 ++++ imgui.h | 8 ++++++-- imgui_draw.cpp | 13 +++++++------ 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f6207965e..f45fa178c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,12 @@ Breaking changes: - TreeNode: renamed ImGuiTreeNodeFlags_NavLeftJumpsBackHere to ImGuiTreeNodeFlags_NavLeftJumpsToParent for clarity. Kept inline redirection enum (will obsolete). (#1079, #8639) +- Fonts: changed ImFont::CalcWordWrapPositionA() to ImFont::CalcWordWrapPosition(): + - old: const char* CalcWordWrapPositionA(float scale, const char* text, ....); + - new: const char* CalcWordWrapPosition (float size, const char* text, ....); + The leading 'float scale' parameters was changed to 'float size'. + This was necessary as 'scale' is assuming standard font size which is a concept we aim to + eliminate in an upcoming update. Kept inline redirection function. - Commented out PushAllowKeyboardFocus()/PopAllowKeyboardFocus() which was obsoleted in 1.89.4 (March 2023). (#3092) - PushAllowKeyboardFocus(bool tab_stop) --> PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); @@ -51,7 +57,7 @@ Breaking changes: in 1.89.6 (June 2023). - ForceDisplayRangeByIndices() --> IncludeItemsByIndex() - Backends: SDL3: Fixed casing typo in function name: (#8509, #8163, #7998, #7988) [@puugz] - - Imgui_ImplSDLGPU3_PrepareDrawData() -> ImGui_ImplSDLGPU3_PrepareDrawData() + - Imgui_ImplSDLGPU3_PrepareDrawData() --> ImGui_ImplSDLGPU3_PrepareDrawData() - Internals: RenderTextEllipsis() function removed the 'float clip_max_x' parameter directly preceding 'float ellipsis_max_x'. Values were identical for a vast majority of users. (#8387) @@ -109,7 +115,7 @@ Other changes: - Fonts: reworked text ellipsis logic to ensure a "..." is always displayed instead of a single character. (#7024) - Fonts: word-wrapping code handle ideographic comma & full stop (U+3001, U+3002). (#8540) -- Fonts: fixed CalcWordWrapPositionA() fallback when width is too small to wrap: +- Fonts: fixed CalcWordWrapPosition() fallback when width is too small to wrap: would use a +1 offset instead of advancing to the next UTF-8 codepoint. (#8540) - Style, InputText: added ImGuiCol_InputTextCursor to configure color of the InputText cursor/caret. (#7031) diff --git a/imgui.cpp b/imgui.cpp index 16632d8da..30263bac6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -431,6 +431,10 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2025/05/23 (1.92.0) - Fonts: changed ImFont::CalcWordWrapPositionA() to ImFont::CalcWordWrapPosition() + - old: const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, ....); + - new: const char* ImFont::CalcWordWrapPosition (float size, const char* text, ....); + The leading 'float scale' parameters was changed to 'float size'. This was necessary as 'scale' is assuming standard font size which is a concept we aim to eliminate in an upcoming update. Kept inline redirection function. - 2025/05/15 (1.92.0) - TreeNode: renamed ImGuiTreeNodeFlags_NavLeftJumpsBackHere to ImGuiTreeNodeFlags_NavLeftJumpsToParent for clarity. Kept inline redirection enum (will obsolete). - 2025/05/15 (1.92.0) - Commented out PushAllowKeyboardFocus()/PopAllowKeyboardFocus() which was obsoleted in 1.89.4. Use PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop)/PopItemFlag() instead. (#3092) - 2025/05/15 (1.92.0) - Commented out ImGuiListClipper::ForceDisplayRangeByIndices() which was obsoleted in 1.89.6. Use ImGuiListClipper::IncludeItemsByIndex() instead. diff --git a/imgui.h b/imgui.h index f4b692ab2..8e0e09b25 100644 --- a/imgui.h +++ b/imgui.h @@ -29,7 +29,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.0 WIP" -#define IMGUI_VERSION_NUM 19196 +#define IMGUI_VERSION_NUM 19197 #define IMGUI_HAS_TABLE /* @@ -3533,10 +3533,14 @@ struct ImFont // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable. // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable. IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL); // utf8 - IMGUI_API const char* CalcWordWrapPositionA(float scale, 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 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); +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + inline const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPosition(FontSize * scale, text, text_end, wrap_width); } +#endif + // [Internal] Don't use! IMGUI_API void BuildLookupTable(); IMGUI_API void ClearOutputData(); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index dbc659d25..78b0e152e 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3936,7 +3936,7 @@ static inline const char* CalcWordWrapNextLineStartA(const char* text, const cha // 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. // 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::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) +const char* ImFont::CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width) { // For references, possible wrap point marked with ^ // "aaa bbb, ccc,ddd. eee fff. ggg!" @@ -3952,6 +3952,7 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c float line_width = 0.0f; float word_width = 0.0f; float blank_width = 0.0f; + const float scale = size / FontSize; wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters const char* word_end = text; @@ -4055,7 +4056,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons { // 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) - word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - line_width); + word_wrap_eol = CalcWordWrapPosition(size, s, text_end, wrap_width - line_width); if (s >= word_wrap_eol) { @@ -4175,10 +4176,10 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im const char* line_end = (const char*)ImMemchr(s, '\n', text_end - s); if (word_wrap_enabled) { - // FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPositionA(). - // If the specs for CalcWordWrapPositionA() were reworked to optionally return on \n we could combine both. + // 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. // However it is still better than nothing performing the fast-forward! - s = CalcWordWrapPositionA(scale, s, line_end ? line_end : text_end, wrap_width); + s = CalcWordWrapPosition(size, s, line_end ? line_end : text_end, wrap_width); s = CalcWordWrapNextLineStartA(s, text_end); } else @@ -4223,7 +4224,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im { // 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) - word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - origin_x)); + word_wrap_eol = CalcWordWrapPosition(size, s, text_end, wrap_width - (x - origin_x)); if (s >= word_wrap_eol) { From 77f1d3b317c400c34ee02fe9a5354d0d757b55ca Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 26 May 2025 19:32:28 +0200 Subject: [PATCH 05/11] Refactor: move SetCurrentFont(), PushFont(), PopFont() to a section. --- imgui.cpp | 121 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 68 insertions(+), 53 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 30263bac6..2332073a2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -77,6 +77,7 @@ CODE // [SECTION] RENDER HELPERS // [SECTION] INITIALIZATION, SHUTDOWN // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) +// [SECTION] FONTS // [SECTION] ID STACK // [SECTION] INPUTS // [SECTION] ERROR CHECKING, STATE RECOVERY @@ -1268,6 +1269,7 @@ static void UpdateMouseWheel(); static void UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt); // Misc +static void UpdateFontsNewFrame(); static void UpdateSettings(); static int UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); static void RenderWindowOuterBorders(ImGuiWindow* window); @@ -5218,10 +5220,8 @@ void ImGui::NewFrame() UpdateViewportsNewFrame(); // Setup current font and draw list shared data - g.IO.Fonts->Locked = true; SetupDrawListSharedData(); - SetCurrentFont(GetDefaultFont()); - IM_ASSERT(g.Font->IsLoaded()); + UpdateFontsNewFrame(); // Mark rendering data as invalid to prevent user who may have a handle on it to use it. for (ImGuiViewportP* viewport : g.Viewports) @@ -7872,56 +7872,6 @@ void ImGui::End() SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window); } -// Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only. -void ImGui::SetCurrentFont(ImFont* font) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? - IM_ASSERT(font->Scale > 0.0f); - g.Font = font; - g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale); - g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; - g.FontScale = g.FontSize / g.Font->FontSize; - - ImFontAtlas* atlas = g.Font->ContainerAtlas; - g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; - g.DrawListSharedData.TexUvLines = atlas->TexUvLines; - g.DrawListSharedData.Font = g.Font; - g.DrawListSharedData.FontSize = g.FontSize; - g.DrawListSharedData.FontScale = g.FontScale; -} - -// Use ImDrawList::_SetTextureID(), making our shared g.FontStack[] authoritative against window-local ImDrawList. -// - Whereas ImDrawList::PushTextureID()/PopTextureID() is not to be used across Begin() calls. -// - Note that we don't propagate current texture id when e.g. Begin()-ing into a new window, we never really did... -// - Some code paths never really fully worked with multiple atlas textures. -// - The right-ish solution may be to remove _SetTextureID() and make AddText/RenderText lazily call PushTextureID()/PopTextureID() -// the same way AddImage() does, but then all other primitives would also need to? I don't think we should tackle this problem -// because we have a concrete need and a test bed for multiple atlas textures. -void ImGui::PushFont(ImFont* font) -{ - ImGuiContext& g = *GImGui; - if (font == NULL) - font = GetDefaultFont(); - g.FontStack.push_back(font); - SetCurrentFont(font); - g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID); -} - -void ImGui::PopFont() -{ - ImGuiContext& g = *GImGui; - if (g.FontStack.Size <= 0) - { - IM_ASSERT_USER_ERROR(0, "Calling PopFont() too many times!"); - return; - } - g.FontStack.pop_back(); - ImFont* font = g.FontStack.Size == 0 ? GetDefaultFont() : g.FontStack.back(); - SetCurrentFont(font); - g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID); -} - void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) { ImGuiContext& g = *GImGui; @@ -8546,6 +8496,71 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) return window->ClipRect.Overlaps(ImRect(rect_min, rect_max)); } +//----------------------------------------------------------------------------- +// [SECTION] FONTS +//----------------------------------------------------------------------------- +// Most of the relevant font logic is in imgui_draw.cpp. +// Those are high-level support functions. +//----------------------------------------------------------------------------- + +void ImGui::UpdateFontsNewFrame() +{ + ImGuiContext& g = *GImGui; + g.IO.Fonts->Locked = true; + SetCurrentFont(GetDefaultFont()); + IM_ASSERT(g.Font->IsLoaded()); +} + +// Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only. +void ImGui::SetCurrentFont(ImFont* font) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? + IM_ASSERT(font->Scale > 0.0f); + g.Font = font; + g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale); + g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; + g.FontScale = g.FontSize / g.Font->FontSize; + + ImFontAtlas* atlas = g.Font->ContainerAtlas; + g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; + g.DrawListSharedData.TexUvLines = atlas->TexUvLines; + g.DrawListSharedData.Font = g.Font; + g.DrawListSharedData.FontSize = g.FontSize; + g.DrawListSharedData.FontScale = g.FontScale; +} + +// Use ImDrawList::_SetTextureID(), making our shared g.FontStack[] authoritative against window-local ImDrawList. +// - Whereas ImDrawList::PushTextureID()/PopTextureID() is not to be used across Begin() calls. +// - Note that we don't propagate current texture id when e.g. Begin()-ing into a new window, we never really did... +// - Some code paths never really fully worked with multiple atlas textures. +// - The right-ish solution may be to remove _SetTextureID() and make AddText/RenderText lazily call PushTextureID()/PopTextureID() +// the same way AddImage() does, but then all other primitives would also need to? I don't think we should tackle this problem +// because we have a concrete need and a test bed for multiple atlas textures. +void ImGui::PushFont(ImFont* font) +{ + ImGuiContext& g = *GImGui; + if (font == NULL) + font = GetDefaultFont(); + g.FontStack.push_back(font); + SetCurrentFont(font); + g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID); +} + +void ImGui::PopFont() +{ + ImGuiContext& g = *GImGui; + if (g.FontStack.Size <= 0) + { + IM_ASSERT_USER_ERROR(0, "Calling PopFont() too many times!"); + return; + } + g.FontStack.pop_back(); + ImFont* font = g.FontStack.Size == 0 ? GetDefaultFont() : g.FontStack.back(); + SetCurrentFont(font); + g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID); +} + //----------------------------------------------------------------------------- // [SECTION] ID STACK //----------------------------------------------------------------------------- From 87a6443c5bc6297f18cbbb2712db2dc28983d23f Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 30 May 2025 20:47:50 +0200 Subject: [PATCH 06/11] Scroll: fixed contents size, scrollbar visibility and scrolling reet issue with abnormally large contents ranges. (#3609, #8215) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 10 +++++----- imgui_internal.h | 2 ++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f45fa178c..4b123e59a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -106,6 +106,8 @@ Other changes: windows (e.g. determined programmatically) can lead to renderer backend trying to create abnormally large framebuffers. - TextLinkOpenURL(): added bool return value on click. (#8645, #8451, #7660) +- Scroll: fixed contents size, scrollbar visibility and scrolling resetting issues + with abnormally large contents ranges. (#3609, #8215) - Nav: fixed assertion when holding gamepad FaceLeft/West button to open CTRL+Tab windowing + pressing a keyboard key. (#8525) - Error Handling: added better error report and recovery for extraneous diff --git a/imgui.cpp b/imgui.cpp index 2332073a2..4e85502be 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6390,10 +6390,10 @@ static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_cur return; } - content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x); - content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y); - content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x); - content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y); + content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : ImTrunc64(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x); + content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : ImTrunc64(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y); + content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : ImTrunc64(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x); + content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : ImTrunc64(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y); } static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents) @@ -11166,7 +11166,7 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) } scroll[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]); } - scroll[axis] = IM_ROUND(ImMax(scroll[axis], 0.0f)); + scroll[axis] = ImRound64(ImMax(scroll[axis], 0.0f)); if (!window->Collapsed && !window->SkipItems) scroll[axis] = ImMin(scroll[axis], window->ScrollMax[axis]); } diff --git a/imgui_internal.h b/imgui_internal.h index fb23c6cfb..ca250e0b7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -496,6 +496,8 @@ static inline float ImTrunc(float f) static inline ImVec2 ImTrunc(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); } static inline float ImFloor(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf() static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); } +static inline float ImTrunc64(float f) { return (float)(ImS64)(f); } +static inline float ImRound64(float f) { return (float)(ImS64)(f + 0.5f); } static inline int ImModPositive(int a, int b) { return (a + b) % b; } static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } From c53c9a8644e05a9e4a0573896eb79d5aeaa354c8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 30 May 2025 21:11:23 +0200 Subject: [PATCH 07/11] Clipper: further mitigation/improvements for abnormally large contents ranges (larger than e.g. 2^31). (#3609, #8215) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 11 +++++++++-- imgui.h | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4b123e59a..8fd49b525 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -108,6 +108,7 @@ Other changes: - TextLinkOpenURL(): added bool return value on click. (#8645, #8451, #7660) - Scroll: fixed contents size, scrollbar visibility and scrolling resetting issues with abnormally large contents ranges. (#3609, #8215) +- Clipper: some mitigation/improvements for abnormally large contents ranges. (#3609, #8215) - Nav: fixed assertion when holding gamepad FaceLeft/West button to open CTRL+Tab windowing + pressing a keyboard key. (#8525) - Error Handling: added better error report and recovery for extraneous diff --git a/imgui.cpp b/imgui.cpp index 4e85502be..fb4966292 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3195,10 +3195,17 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) if (table) IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y); - clipper->ItemsHeight = (window->DC.CursorPos.y - clipper->StartPosY) / (float)(clipper->DisplayEnd - clipper->DisplayStart); - bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); + bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision((float)clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); if (affected_by_floating_point_precision) + { + // Mitigation/hack for very large range: assume last time height constitute line height. clipper->ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries. + window->DC.CursorPos.y = (float)(clipper->StartPosY + clipper->ItemsHeight); + } + else + { + clipper->ItemsHeight = (float)(window->DC.CursorPos.y - clipper->StartPosY) / (float)(clipper->DisplayEnd - clipper->DisplayStart); + } if (clipper->ItemsHeight == 0.0f && clipper->ItemsCount == INT_MAX) // Accept that no item have been submitted if in indeterminate mode. return false; IM_ASSERT(clipper->ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); diff --git a/imgui.h b/imgui.h index 8e0e09b25..6d062c190 100644 --- a/imgui.h +++ b/imgui.h @@ -2708,7 +2708,7 @@ struct ImGuiListClipper int DisplayEnd; // End of items to display (exclusive) int ItemsCount; // [Internal] Number of items float ItemsHeight; // [Internal] Height of item after a first step and item submission can calculate it - float StartPosY; // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed + double StartPosY; // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed double StartSeekOffsetY; // [Internal] Account for frozen rows in a table and initial loss of precision in very large windows. void* TempData; // [Internal] Internal data From 19289d587a3a7a13c4ca799dbf2108c2a5bb4935 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 30 May 2025 21:33:09 +0200 Subject: [PATCH 08/11] Nav: fixed scroll fallback (when there are no interactive widgets to jump to) not being enabled on windows with menu or title bar. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8fd49b525..92df7b4f6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -111,6 +111,8 @@ Other changes: - Clipper: some mitigation/improvements for abnormally large contents ranges. (#3609, #8215) - Nav: fixed assertion when holding gamepad FaceLeft/West button to open CTRL+Tab windowing + pressing a keyboard key. (#8525) +- Nav: fixed scroll fallback (when there are no interactive widgets to jump to) not + being enabled on windows with menu or title bar. - Error Handling: added better error report and recovery for extraneous EndPopup() call. (#1651, #8499) - Error Handling: added better error report and recovery when calling EndFrame() diff --git a/imgui.cpp b/imgui.cpp index fb4966292..36d2e976e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13598,7 +13598,7 @@ static float ImGui::NavUpdatePageUpPageDown() if (g.NavLayer != ImGuiNavLayer_Main) NavRestoreLayer(ImGuiNavLayer_Main); - if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY) + if ((window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Main)) == 0 && window->DC.NavWindowHasScrollY) { // Fallback manual-scroll when window has no navigable item if (IsKeyPressed(ImGuiKey_PageUp, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner)) From c3a3a39e92adaf64272c5c3542844852a52d0ec6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 30 May 2025 21:46:33 +0200 Subject: [PATCH 09/11] Nav: fixed abnormal clipping disable over large ranges, could lead to stall. (#3841, #1725) Amend 93cccd27f --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 92df7b4f6..1f03f7936 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -113,6 +113,8 @@ Other changes: CTRL+Tab windowing + pressing a keyboard key. (#8525) - Nav: fixed scroll fallback (when there are no interactive widgets to jump to) not being enabled on windows with menu or title bar. +- Nav: fixed an issue handling PageUp/PageDown on windows with abnormally large contents + range which could lead to clipper requesting very large ranges. - Error Handling: added better error report and recovery for extraneous EndPopup() call. (#1651, #8499) - Error Handling: added better error report and recovery when calling EndFrame() diff --git a/imgui.cpp b/imgui.cpp index 36d2e976e..419098b80 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3229,7 +3229,10 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) // Add range selected to be included for navigation const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); if (is_nav_request) + { + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringRect.Min.y, g.NavScoringRect.Max.y, 0, 0)); data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0)); + } if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1) data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount)); @@ -13379,7 +13382,7 @@ void ImGui::NavUpdateCreateMoveRequest() //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG] } g.NavScoringRect = scoring_rect; - g.NavScoringNoClipRect.Add(scoring_rect); + //g.NavScoringNoClipRect.Add(scoring_rect); } void ImGui::NavUpdateCreateTabbingRequest() From e6913f58b90f9eaf034fab5b576ca8ef6bd80aa7 Mon Sep 17 00:00:00 2001 From: Romain Moret Date: Mon, 26 May 2025 19:02:18 +0200 Subject: [PATCH 10/11] imgui_freetype: Update lunasvg API to support v3.0+ (#8656, #6842, #6591) --- docs/CHANGELOG.txt | 1 + misc/freetype/imgui_freetype.cpp | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1f03f7936..aaf219a6e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -268,6 +268,7 @@ Other changes: - Debug Tools: Added io.ConfigDebugHighlightIdConflictsShowItemPicker (defaults to true) to allow disabled Item Picker suggestion in user facing builds. (#7961, #7669) - Debug Tools: Tweaked layout of ID Stack Tool and always display full path. (#4631) +- imgui_freetype: update lunasvg API to support v3.0+. (#8656, #6842, #6591) - Misc: Various zealous warning fixes for newer version of Clang. - Misc: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursors (busy/wait/hourglass shape, with or without an arrow cursor). diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index 73b9862bc..39a997e67 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -880,8 +880,12 @@ static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state) // rows is height, pitch (or stride) equals to width * sizeof(int32) lunasvg::Bitmap bitmap((uint8_t*)slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch); +#if LUNASVG_VERSION_MAJOR >= 3 + state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated +#else state->svg->setMatrix(state->svg->matrix().identity()); // Reset the svg matrix to the default value state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated +#endif state->err = FT_Err_Ok; return state->err; } @@ -904,7 +908,11 @@ static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_ return state->err; } +#if LUNASVG_VERSION_MAJOR >= 3 + lunasvg::Box box = state->svg->boundingBox(); +#else lunasvg::Box box = state->svg->box(); +#endif double scale = std::min(metrics.x_ppem / box.w, metrics.y_ppem / box.h); double xx = (double)document->transform.xx / (1 << 16); double xy = -(double)document->transform.xy / (1 << 16); @@ -913,6 +921,15 @@ static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_ double x0 = (double)document->delta.x / 64 * box.w / metrics.x_ppem; double y0 = -(double)document->delta.y / 64 * box.h / metrics.y_ppem; +#if LUNASVG_VERSION_MAJOR >= 3 + // Scale, transform and pre-translate the matrix for the rendering step + state->matrix = lunasvg::Matrix::translated(-box.x, -box.y); + state->matrix.multiply(lunasvg::Matrix(xx, xy, yx, yy, x0, y0)); + state->matrix.scale(scale, scale); + + // Apply updated transformation to the bounding box + box.transform(state->matrix); +#else // Scale and transform, we don't translate the svg yet state->matrix.identity(); state->matrix.scale(scale, scale); @@ -924,6 +941,7 @@ static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_ // Get the box again after the transformation box = state->svg->box(); +#endif // Calculate the bitmap size slot->bitmap_left = FT_Int(box.x); From 69e1fb50cacbde1c2c585ae59898e68c1818d9b7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 30 May 2025 21:59:13 +0200 Subject: [PATCH 11/11] Docs: fixed missing commit credit. (#8656) --- docs/CHANGELOG.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index aaf219a6e..956c04120 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -268,7 +268,7 @@ Other changes: - Debug Tools: Added io.ConfigDebugHighlightIdConflictsShowItemPicker (defaults to true) to allow disabled Item Picker suggestion in user facing builds. (#7961, #7669) - Debug Tools: Tweaked layout of ID Stack Tool and always display full path. (#4631) -- imgui_freetype: update lunasvg API to support v3.0+. (#8656, #6842, #6591) +- imgui_freetype: update lunasvg API to support v3.0+. (#8656, #6842, #6591) [@moretromain] - Misc: Various zealous warning fixes for newer version of Clang. - Misc: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursors (busy/wait/hourglass shape, with or without an arrow cursor).