From 1e118ab8911625bf49938dd5d29c2833af216096 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 26 May 2025 20:24:06 +0200 Subject: [PATCH] Fonts: added ImGuiStyle::FontSizeBase. Ensuring PushFontSize() works before main loop and across NewFrame(). # Conflicts: # imgui.cpp --- imgui.cpp | 106 +++++++++++++++++++++++++++++++++-------------- imgui.h | 20 +++++++-- imgui_demo.cpp | 44 +++++++++++++------- imgui_draw.cpp | 2 +- imgui_internal.h | 9 ++-- 5 files changed, 125 insertions(+), 56 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7b4fc8638..d18beab96 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1354,6 +1354,7 @@ static void* GImAllocatorUserData = NULL; ImGuiStyle::ImGuiStyle() { + FontSizeBase = 0.0f; // Will default to io.Fonts->Fonts[0] on first frame. Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui. DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha. WindowPadding = ImVec2(8,8); // Padding within a window @@ -1415,6 +1416,10 @@ ImGuiStyle::ImGuiStyle() HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse. HoverFlagsForTooltipNav = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad. + // [Internal] + _MainScale = 1.0f; + _NextFrameFontSizeBase = 0.0f; + // Default theme ImGui::StyleColorsDark(this); } @@ -1423,6 +1428,8 @@ ImGuiStyle::ImGuiStyle() // Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times. void ImGuiStyle::ScaleAllSizes(float scale_factor) { + _MainScale *= scale_factor; + FontSizeBase = ImTrunc(FontSizeBase * scale_factor); WindowPadding = ImTrunc(WindowPadding * scale_factor); WindowRounding = ImTrunc(WindowRounding * scale_factor); WindowMinSize = ImTrunc(WindowMinSize * scale_factor); @@ -4403,7 +4410,7 @@ static void SetCurrentWindow(ImGuiWindow* window) ImGuiViewport* viewport = window->Viewport; g.FontRasterizerDensity = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale.x : g.IO.DisplayFramebufferScale.x; // == SetFontRasterizerDensity() } - ImGui::UpdateCurrentFontSize(); + ImGui::UpdateCurrentFontSize(0.0f); ImGui::NavUpdateCurrentWindowIsScrollPushableX(); } } @@ -8450,7 +8457,7 @@ void ImGui::SetWindowFontScale(float scale) IM_ASSERT(scale > 0.0f); ImGuiWindow* window = GetCurrentWindow(); window->FontWindowScale = scale; - UpdateCurrentFontSize(); + UpdateCurrentFontSize(0.0f); } void ImGui::PushFocusScope(ImGuiID id) @@ -8619,12 +8626,24 @@ void ImGui::UpdateFontsNewFrame() for (ImFontAtlas* atlas : g.FontAtlases) atlas->Locked = true; - // We do this really unusual thing of calling *push_front()*, the reason behind that we want to support the PushFont()/NewFrame()/PopFont() idiom. - ImFontStackData font_stack_data = { ImGui::GetDefaultFont(), ImGui::GetDefaultFont()->LegacySize }; - g.FontStack.push_front(font_stack_data); - if (g.FontStack.Size == 1) - ImGui::SetCurrentFont(font_stack_data.Font, font_stack_data.FontSize); + if (g.Style._NextFrameFontSizeBase != 0.0f) + { + g.Style.FontSizeBase = g.Style._NextFrameFontSizeBase; + g.Style._NextFrameFontSizeBase = 0.0f; + } + // Apply default font size the first time + ImFont* font = ImGui::GetDefaultFont(); + if (g.Style.FontSizeBase <= 0.0f) + g.Style.FontSizeBase = font->LegacySize * g.Style._MainScale; + + // Set initial font + g.Font = font; + g.FontSizeBeforeScaling = g.Style.FontSizeBase; + g.FontSize = 0.0f; + ImFontStackData font_stack_data = { font, g.Style.FontSizeBase, g.Style.FontSizeBase }; // <--- Will restore FontSize + SetCurrentFont(font_stack_data.Font, font_stack_data.FontSizeBeforeScaling, 0.0f); // <--- but use 0.0f to enable scale + g.FontStack.push_back(font_stack_data); IM_ASSERT(g.Font->IsLoaded()); } @@ -8633,6 +8652,15 @@ void ImGui::UpdateFontsEndFrame() PopFont(); } +ImFont* ImGui::GetDefaultFont() +{ + ImGuiContext& g = *GImGui; + ImFontAtlas* atlas = g.IO.Fonts; + if (atlas->Builder == NULL) + ImFontAtlasBuildMain(atlas); + return g.IO.FontDefault ? g.IO.FontDefault : atlas->Fonts[0]; +} + void ImGui::RegisterUserTexture(ImTextureData* tex) { ImGuiContext& g = *GImGui; @@ -8673,12 +8701,12 @@ void ImGui::UnregisterFontAtlas(ImFontAtlas* atlas) // 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. // FIXME-NEWATLAS-V2: perhaps we can now leverage ImFontAtlasUpdateDrawListsTextures() ? -void ImGui::SetCurrentFont(ImFont* font, float font_size) +void ImGui::SetCurrentFont(ImFont* font, float font_size_before_scaling, float font_size_after_scaling) { ImGuiContext& g = *GImGui; g.Font = font; - g.FontSizeBeforeScaling = font_size; - UpdateCurrentFontSize(); + g.FontSizeBeforeScaling = font_size_before_scaling; + UpdateCurrentFontSize(font_size_after_scaling); if (font != NULL) { @@ -8693,20 +8721,27 @@ void ImGui::SetCurrentFont(ImFont* font, float font_size) } } -void ImGui::UpdateCurrentFontSize() +void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + + g.Style.FontSizeBase = g.FontSizeBeforeScaling; if (window != NULL && window->SkipItems) return; - float final_size = g.FontSizeBeforeScaling * g.IO.FontGlobalScale; + // Restoring is pretty much only used by PopFont()/PopFontSize() + float final_size = (restore_font_size_after_scaling > 0.0f) ? restore_font_size_after_scaling : 0.0f; + if (final_size == 0.0f) + { + final_size = g.FontSizeBeforeScaling * g.IO.FontGlobalScale; #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - if (g.Font != NULL) - final_size *= g.Font->Scale; + if (g.Font != NULL) + final_size *= g.Font->Scale; #endif - if (window != NULL) - final_size *= window->FontWindowScale; + if (window != NULL) + final_size *= window->FontWindowScale; + } // Round font size // - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet. @@ -8731,12 +8766,16 @@ void ImGui::SetFontRasterizerDensity(float rasterizer_density) if (g.FontRasterizerDensity == rasterizer_density) return; g.FontRasterizerDensity = rasterizer_density; - UpdateCurrentFontSize(); + UpdateCurrentFontSize(0.0f); } +// If you want to scale an existing font size: +// - Use e.g. PushFontSize(style.FontSizeBase * factor) (= value before external scale factors applied). +// - Do NOT use PushFontSize(GetFontSize() * factor) (= value after external scale factors applied). void ImGui::PushFont(ImFont* font, float font_size) { ImGuiContext& g = *GImGui; + g.FontStack.push_back({ g.Font, g.FontSizeBeforeScaling, g.FontSize }); if (font == NULL) font = GetDefaultFont(); if (font_size <= 0.0f) @@ -8746,23 +8785,20 @@ void ImGui::PushFont(ImFont* font, float font_size) else font_size = g.FontSizeBeforeScaling; // Keep current font size } - g.FontStack.push_back({ font, font_size }); - SetCurrentFont(font, font_size); + SetCurrentFont(font, font_size, 0.0f); } void ImGui::PopFont() { ImGuiContext& g = *GImGui; - if (g.FontStack.Size <= 1 && g.WithinFrameScope) + if (g.FontStack.Size <= 0 && g.WithinFrameScope) { IM_ASSERT_USER_ERROR(0, "Calling PopFont() too many times!"); return; } + ImFontStackData* font_stack_data = &g.FontStack.back(); + SetCurrentFont(font_stack_data->Font, font_stack_data->FontSizeBeforeScaling, font_stack_data->FontSizeAfterScaling); g.FontStack.pop_back(); - if (ImFontStackData* font_stack_data = (g.FontStack.Size > 0) ? &g.FontStack.back() : NULL) - SetCurrentFont(font_stack_data->Font, font_stack_data->FontSize); - else - SetCurrentFont(NULL, 0.0f); } void ImGui::PushFontSize(float font_size) @@ -15751,10 +15787,11 @@ void ImGui::ShowFontAtlas(ImFontAtlas* atlas) { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; + ImGuiStyle& style = g.Style; Text("Read "); SameLine(0, 0); TextLinkOpenURL("https://www.dearimgui.com/faq/"); SameLine(0, 0); - Text(" for details on font loading."); + Text(" for details."); SeparatorText("Backend Support for Dynamic Fonts"); BeginDisabled(); @@ -15762,11 +15799,16 @@ void ImGui::ShowFontAtlas(ImFontAtlas* atlas) EndDisabled(); BeginDisabled((io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0); - SetNextItemWidth(GetFontSize() * 5); - DragFloat("io.FontGlobalScale", &io.FontGlobalScale, 0.05f, 0.5f, 5.0f); - BulletText("This is scaling font only. General scaling will come later."); - BulletText("Load an actual font that's not the default for best result!"); - BulletText("Please submit feedback:"); SameLine(); TextLinkOpenURL("https://github.com/ocornut/imgui/issues/8465"); + SetNextItemWidth(GetFontSize() * 10); + if (DragFloat("FontSizeBase", &style.FontSizeBase, 0.20f, 5.0f, 100.0f, "%.0f")) + style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work. + SameLine(0.0f, 0.0f); Text(" (out %.2f)", GetFontSize()); + SameLine(); MetricsHelpMarker("- This is scaling font only. General scaling will come later."); + SetNextItemWidth(GetFontSize() * 10); + DragFloat("io.FontGlobalScale", &io.FontGlobalScale, 0.05f, 0.5f, 5.0f); // <-- This works, but no need to make it too visible. + BulletText("Load a nice font for better results!"); + BulletText("Please submit feedback:"); + SameLine(); TextLinkOpenURL("#8465", "https://github.com/ocornut/imgui/issues/8465"); EndDisabled(); SeparatorText("Fonts"); @@ -15786,7 +15828,7 @@ void ImGui::ShowFontAtlas(ImFontAtlas* atlas) #else BeginDisabled(); RadioButton("stb_truetype", false); - SetItemTooltip("Requires IMGUI_ENABLE_STB_TRUETYPE"); + SetItemTooltip("Requires #define IMGUI_ENABLE_STB_TRUETYPE"); EndDisabled(); #endif SameLine(); @@ -15810,7 +15852,7 @@ void ImGui::ShowFontAtlas(ImFontAtlas* atlas) #else BeginDisabled(); RadioButton("FreeType", false); - SetItemTooltip("Requires IMGUI_ENABLE_FREETYPE + imgui_freetype.cpp."); + SetItemTooltip("Requires #define IMGUI_ENABLE_FREETYPE + imgui_freetype.cpp."); EndDisabled(); #endif EndDisabled(); diff --git a/imgui.h b/imgui.h index 44aa078ae..d2bc7ce77 100644 --- a/imgui.h +++ b/imgui.h @@ -498,8 +498,13 @@ namespace ImGui // *IMPORTANT* before 1.92, fonts had a single size. They can now be dynamically be adjusted. // - Before 1.92: PushFont() always used font default size. // - Since 1.92: PushFont() preserve the current shared font size. - // - To use old behavior (single size font): use 'PushFont(font, font->LegacySize)' in call site, or set 'ImFontConfig::Flags |= ImFontFlags_DefaultToLegacySize' before calling AddFont(). - IMGUI_API void PushFont(ImFont* font, float font_size = -1); // use NULL as a shortcut to push default font. Use <0.0f to keep current font size. Use font->LegacySize to revert to font size specified by AddFont(). + // - To use old behavior (single size font, size specified in AddFontXXX() call: + // - Use 'PushFont(font, font->LegacySize)' at call site + // - Or set 'ImFontConfig::Flags |= ImFontFlags_DefaultToLegacySize' before calling AddFont(), and then 'PushFont(font)' will use this size. + // *IMPORTANT* If you want to scale an existing font size: + // - OK: PushFontSize(style.FontSizeBase * factor) (= value before external scale factors applied). + // - KO: PushFontSize(GetFontSize() * factor) (= value after external scale factors applied. external scale factors are io.FontGlobalScale and per-viewport scales.). + IMGUI_API void PushFont(ImFont* font, float font_size = -1); // use NULL as a shortcut to push default font. Use <0.0f to keep current font size. IMGUI_API void PopFont(); IMGUI_API void PushFontSize(float font_size); IMGUI_API void PopFontSize(); @@ -2222,6 +2227,8 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE struct ImGuiStyle { + float FontSizeBase; // Current base font size (scaling applied). Use PushFont()/PushFontSize() to modify. Use ImGui::GetFontSize() to obtain scaled value. + float Alpha; // Global alpha applies to everything in Dear ImGui. float DisabledAlpha; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha. ImVec2 WindowPadding; // Padding within a window. @@ -2287,8 +2294,13 @@ struct ImGuiStyle ImGuiHoveredFlags HoverFlagsForTooltipMouse;// Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse. ImGuiHoveredFlags HoverFlagsForTooltipNav; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad. - IMGUI_API ImGuiStyle(); - IMGUI_API void ScaleAllSizes(float scale_factor); + // [Internal] + float _MainScale; // FIXME-WIP: Reference scale, as applied by ScaleAllSizes(). + float _NextFrameFontSizeBase; // FIXME: Temporary hack until we finish remaining work. + + // Functions + IMGUI_API ImGuiStyle(); + IMGUI_API void ScaleAllSizes(float scale_factor); // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS diff --git a/imgui_demo.cpp b/imgui_demo.cpp index dda817eb8..fc1d16953 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -430,16 +430,25 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Text("dear imgui says hello! (%s) (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); ImGui::Spacing(); - ImGui::SeparatorText("dynamic_fonts branch"); - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 5); - ImGui::DragFloat("io.FontGlobalScale", &ImGui::GetIO().FontGlobalScale, 0.05f, 0.5f, 5.0f); - ImGui::BulletText("This is scaling font only. General scaling will come later."); - ImGui::BulletText("Load an actual font that's not the default for best result!"); - ImGui::BulletText("See 'Widgets->Fonts' below for more.."); - ImGui::BulletText("Current font loader: '%s'", ImGui::GetIO().Fonts->FontLoaderName); - ImGui::BulletText("Please submit feedback:"); ImGui::SameLine(); - ImGui::TextLinkOpenURL("https://github.com/ocornut/imgui/issues/8465"); - ImGui::Spacing(); + { + ImGui::SeparatorText("dynamic_fonts branch"); + ImGuiStyle& style = ImGui::GetStyle(); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20); + ImGui::ShowFontSelector("Font"); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20); + if (ImGui::DragFloat("FontSizeBase", &style.FontSizeBase, 0.20f, 5.0f, 100.0f, "%.0f")) + style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work. + ImGui::SameLine(0.0f, 0.0f); Text(" (out %.2f)", ImGui::GetFontSize()); + ImGui::SameLine(); HelpMarker("- This is scaling font only. General scaling will come later."); + //ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20); + //ImGui::DragFloat("FontGlobalScale", &ImGui::GetIO().FontGlobalScale, 0.05f, 0.5f, 5.0f); + ImGui::BulletText("Load a nice font for better results!"); + ImGui::BulletText("See 'Widgets->Fonts' below for more."); + //ImGui::BulletText("Current font loader: '%s'", ImGui::GetIO().Fonts->FontLoaderName); + ImGui::BulletText("Please submit feedback:"); ImGui::SameLine(); + ImGui::TextLinkOpenURL("#8465", "https://github.com/ocornut/imgui/issues/8465"); + ImGui::Spacing(); + } IMGUI_DEMO_MARKER("Help"); if (ImGui::CollapsingHeader("Help")) @@ -8207,11 +8216,16 @@ void ImGui::ShowFontSelector(const char* label) ImGui::EndCombo(); } ImGui::SameLine(); - HelpMarker( - "- Load additional fonts with io.Fonts->AddFontFromFileTTF().\n" - "- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n" - "- Read FAQ and docs/FONTS.md for more details.\n" - "- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame()."); + if (io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) + HelpMarker( + "- Load additional fonts with io.Fonts->AddFontXXX() functions.\n" + "- Read FAQ and docs/FONTS.md for more details."); + else + HelpMarker( + "- Load additional fonts with io.Fonts->AddFontXXX() functions.\n" + "- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n" + "- Read FAQ and docs/FONTS.md for more details.\n" + "- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame()."); } // Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options. diff --git a/imgui_draw.cpp b/imgui_draw.cpp index e067d57ba..69c207fd2 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3190,7 +3190,7 @@ static void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, bool need_bind_ctx = ctx != curr_ctx; if (need_bind_ctx) ImGui::SetCurrentContext(ctx); - ImGui::SetCurrentFont(new_font, ctx->FontSize); + ImGui::SetCurrentFont(new_font, ctx->FontSizeBeforeScaling, ctx->FontSize); if (need_bind_ctx) ImGui::SetCurrentContext(curr_ctx); } diff --git a/imgui_internal.h b/imgui_internal.h index 5dacf666e..4c1d1f392 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -871,7 +871,8 @@ struct ImDrawDataBuilder struct ImFontStackData { ImFont* Font; - float FontSize; + float FontSizeBeforeScaling; + float FontSizeAfterScaling; }; //----------------------------------------------------------------------------- @@ -3116,12 +3117,12 @@ namespace ImGui IMGUI_API void UnregisterUserTexture(ImTextureData* tex); IMGUI_API void RegisterFontAtlas(ImFontAtlas* atlas); IMGUI_API void UnregisterFontAtlas(ImFontAtlas* atlas); - IMGUI_API void SetCurrentFont(ImFont* font, float font_size); + IMGUI_API void SetCurrentFont(ImFont* font, float font_size_before_scaling, float font_size_after_scaling); + IMGUI_API void UpdateCurrentFontSize(float restore_font_size_after_scaling); IMGUI_API void SetFontRasterizerDensity(float rasterizer_density); inline float GetFontRasterizerDensity() { return GImGui->FontRasterizerDensity; } - IMGUI_API void UpdateCurrentFontSize(); inline float GetRoundedFontSize(float size) { return IM_ROUND(size); } - inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } + IMGUI_API ImFont* GetDefaultFont(); IMGUI_API void PushPasswordFont(); IMGUI_API void PopPasswordFont(); inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); return GetForegroundDrawList(); } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches.