From daaf0e4ef30678540f3ad862008de218e8fbb0f1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 27 Jan 2025 20:24:05 +0100 Subject: [PATCH] Fonts: Added PushFontSize(), PopFontSize() api. Added font_size param to PushFont() as well. Fonts: Fixed PopFont() recovery. (To squash into "Added PushFontSize(), PopFontSize() api." --- imgui.cpp | 80 ++++++++++++++++++++++++++++++------------------ imgui.h | 14 +++++---- imgui_draw.cpp | 11 ++++--- imgui_internal.h | 8 ++++- 4 files changed, 73 insertions(+), 40 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0b1433857..fb26c967b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1270,6 +1270,7 @@ static void UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt); // Misc static void UpdateFontsNewFrame(); +static void UpdateFontsEndFrame(); static void UpdateTexturesNewFrame(); static void UpdateTexturesEndFrame(); static void UpdateSettings(); @@ -5762,6 +5763,7 @@ void ImGui::EndFrame() // End frame g.WithinFrameScope = false; g.FrameCountEnded = g.FrameCount; + UpdateFontsEndFrame(); // Initiate moving window + handle left-click and right-click focus UpdateMouseMovingWindowEndFrame(); @@ -8579,64 +8581,84 @@ void ImGui::UpdateFontsNewFrame() ImFontAtlas* atlas = g.IO.Fonts; if ((g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0) atlas->Locked = true; - SetCurrentFont(GetDefaultFont(), GetDefaultFont()->Sources[0].SizePixels); + PushFont(GetDefaultFont(), GetDefaultFont()->Sources[0].SizePixels); 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, float font_size) +void ImGui::UpdateFontsEndFrame() { - 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.FontBaked->Size * g.Font->Scale); - g.FontSize = font_size;// g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; - g.FontBaked = g.Font->GetFontBaked(g.FontSize); - g.FontScale = g.FontSize / g.FontBaked->Size; - g.DrawListSharedData.Font = g.Font; - g.DrawListSharedData.FontSize = g.FontSize; - g.DrawListSharedData.FontScale = g.FontScale; - ImFontAtlasUpdateDrawListsSharedData(g.Font->ContainerAtlas); - if (g.CurrentWindow) - g.CurrentWindow->DrawList->_SetTexture(font->ContainerAtlas->TexRef); + PopFont(); } -// 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. +// Use ImDrawList::_SetTexture(), making our shared g.FontStack[] authoritative against window-local ImDrawList. +// - Whereas ImDrawList::PushTexture()/PopTexture() 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 right-ish solution may be to remove _SetTexture() and make AddText/RenderText lazily call PushTexture()/PopTexture() // 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::PushFont(ImFont* font) +void ImGui::SetCurrentFont(ImFont* font, float font_size) +{ + ImGuiContext& g = *GImGui; + g.Font = font; + //g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.FontBaked->FontSize * g.Font->Scale); + g.FontSize = font_size;// g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; + if (font != NULL) + { + IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? + IM_ASSERT(font->Scale > 0.0f); + g.FontBaked = g.Font->GetFontBaked(g.FontSize); + g.FontScale = g.FontSize / g.FontBaked->Size; + g.DrawListSharedData.Font = g.Font; + g.DrawListSharedData.FontSize = g.FontSize; + g.DrawListSharedData.FontScale = g.FontScale; + ImFontAtlasUpdateDrawListsSharedData(g.Font->ContainerAtlas); + if (g.CurrentWindow != NULL) + g.CurrentWindow->DrawList->_SetTexture(font->ContainerAtlas->TexRef); + } + else + { + g.FontBaked = NULL; + g.FontScale = 0.0f; + } +} + +void ImGui::PushFont(ImFont* font, float font_size) { ImGuiContext& g = *GImGui; if (font == NULL) font = GetDefaultFont(); - g.FontStack.push_back(font); - SetCurrentFont(font, g.FontSize); + if (font_size < 0.0f) + font_size = g.FontSize; + g.FontStack.push_back({ font, font_size }); + SetCurrentFont(font, font_size); } void ImGui::PopFont() { ImGuiContext& g = *GImGui; - if (g.FontStack.Size <= 0) + if (g.FontStack.Size <= 1 && g.WithinFrameScope) { 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.FontSize); // FIXME-BAKED: size in stack + 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::SetFontSize(float size) +void ImGui::PushFontSize(float font_size) { - // FIXME-BAKED ImGuiContext& g = *GImGui; - SetCurrentFont(g.Font, size); + PushFont(g.Font, font_size); +} + +void ImGui::PopFontSize() +{ + PopFont(); } //----------------------------------------------------------------------------- diff --git a/imgui.h b/imgui.h index 717c9f0e4..d60deceb9 100644 --- a/imgui.h +++ b/imgui.h @@ -469,11 +469,13 @@ namespace ImGui IMGUI_API void SetScrollFromPosX(float local_x, float center_x_ratio = 0.5f); // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position. IMGUI_API void SetScrollFromPosY(float local_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position. - // Parameters stacks (shared) - IMGUI_API void PushFont(ImFont* font); // use NULL as a shortcut to push default font + // Parameters stacks (font) + 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 SetFontSize(float size); - //IMGUI_API void PopFontSize(); + IMGUI_API void PushFontSize(float size); + IMGUI_API void PopFontSize(); + + // Parameters stacks (shared) IMGUI_API void PushStyleColor(ImGuiCol idx, ImU32 col); // modify a style color. always use this if you modify the style after NewFrame(). IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col); IMGUI_API void PopStyleColor(int count = 1); @@ -3657,8 +3659,8 @@ struct ImFontBaked // Font runtime data and rendering // - ImFontAtlas automatically loads a default embedded font for you if you didn't load one manually. // - Since 1.92.X a font may be rendered as any size! Therefore a font doesn't have one specific size. -// - Use 'font->GetBakedForSize(size)' to retrieve the ImFontBaked* corresponding to a given size. -// - If you used g.Font + g.FontSize (which is frequent from the ImGui layer), you can use g.FontBaked as a shortcut, as g.FontBaked == g.Font->GetBakedForSize(g.FontSize). +// - Use 'font->GetFontBaked(size)' to retrieve the ImFontBaked* corresponding to a given size. +// - If you used g.Font + g.FontSize (which is frequent from the ImGui layer), you can use g.FontBaked as a shortcut, as g.FontBaked == g.Font->GetFontBaked(g.FontSize). struct ImFont { // [Internal] Members: Cold ~32/40/80 bytes diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 7c7a7916a..362697609 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -434,7 +434,7 @@ void ImDrawList::_SetDrawListSharedData(ImDrawListSharedData* data) } // Initialize before use in a new frame. We always have a command ready in the buffer. -// In the majority of cases, you would want to call PushClipRect() and PushTextureID() after this. +// In the majority of cases, you would want to call PushClipRect() and PushTexture() after this. void ImDrawList::_ResetForNewFrame() { // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory. @@ -683,7 +683,7 @@ void ImDrawList::PopTexture() _OnChangedTexture(); } -// This is used by ImGui::PushFont()/PopFont(). It works because we never use _TextureIdStack[] elsewhere than in PushTextureID()/PopTextureID(). +// This is used by ImGui::PushFont()/PopFont(). It works because we never use _TextureIdStack[] elsewhere than in PushTexture()/PopTexture(). void ImDrawList::_SetTexture(ImTextureRef tex_ref) { if (_CmdHeader.TexRef == tex_ref) @@ -3042,7 +3042,7 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template) if (font_cfg.SizePixels <= 0.0f) font_cfg.SizePixels = 13.0f * 1.0f; if (font_cfg.Name[0] == '\0') - ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels); + ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf"); font_cfg.EllipsisChar = (ImWchar)0x0085; font_cfg.GlyphOffset.y = 1.0f * IM_TRUNC(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units @@ -3074,7 +3074,7 @@ ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, // Store a short copy of filename into into the font name for convenience const char* p; for (p = filename + ImStrlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {} - ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s, %.0fpx", p, size_pixels); + ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s", p); } return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, &font_cfg, glyph_ranges); } @@ -3137,6 +3137,9 @@ static void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, if (need_bind_ctx) ImGui::SetCurrentContext(curr_ctx); } + for (ImFontStackData& font_stack_data : ctx->FontStack) + if (font_stack_data.Font == old_font) + font_stack_data.Font = new_font; } } } diff --git a/imgui_internal.h b/imgui_internal.h index 5075028ca..f0812d71f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -869,6 +869,12 @@ struct ImDrawDataBuilder ImDrawDataBuilder() { memset(this, 0, sizeof(*this)); } }; +struct ImFontStackData +{ + ImFont* Font; + float FontSize; +}; + //----------------------------------------------------------------------------- // [SECTION] Style support //----------------------------------------------------------------------------- @@ -2237,7 +2243,7 @@ struct ImGuiContext ImGuiCol DebugFlashStyleColorIdx; // (Keep close to ColorStack to share cache line) ImVector ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin() ImVector StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin() - ImVector FontStack; // Stack for PushFont()/PopFont() - inherited by Begin() + ImVector FontStack; // Stack for PushFont()/PopFont() - inherited by Begin() ImVector FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin() ImVector ItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin() ImVector GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin()