diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index c7be48626..9868ab456 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -38,8 +38,9 @@ namespace FlaxEditor.GUI { Depth = -1; - if (Height < Style.Current.FontMedium.Height) - Height = Style.Current.FontMedium.Height + 4; + var mediumHeight = FallbackTextUtils.GetMaxHeight(Style.Current.FontMedium); + if (Height < mediumHeight) + Height = mediumHeight + 4; } /// diff --git a/Source/Engine/Render2D/FallbackFonts.h b/Source/Engine/Render2D/FallbackFonts.h index 5861354fc..040ca8087 100644 --- a/Source/Engine/Render2D/FallbackFonts.h +++ b/Source/Engine/Render2D/FallbackFonts.h @@ -9,36 +9,55 @@ struct TextRange; class Font; class FontAsset; +/// +/// Defines a list of fonts that can be used as a fallback, ordered by priority. +/// API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API FallbackFonts : public ManagedScriptingObject { DECLARE_SCRIPTING_TYPE_NO_SPAWN(FallbackFonts); private: - /// - /// The list of fallback fonts, ordered by priority. - /// The first element is reserved for the primary font, fallback fonts starts from the second element. - /// Array _fontAssets; + // Cache fallback fonts of various sizes Dictionary*> _cache; public: + /// + /// Initializes a new instance of the class. + /// + /// The fallback font assets. FallbackFonts(const Array& fonts); + /// + /// Initializes a new instance of the class, exposed for C#. + /// + /// The fallback font assets. + /// The new instance. API_FUNCTION() FORCE_INLINE static FallbackFonts* Create(const Array& fonts) { return New(fonts); } + /// + /// Get the parent assets of fallback fonts. + /// + /// The font assets. API_PROPERTY() FORCE_INLINE Array& GetFonts() { return _fontAssets; } + /// + /// Set the fallback fonts. + /// + /// The parent assets of the new fonts. API_PROPERTY() FORCE_INLINE void SetFonts(const Array& val) { _fontAssets = val; } /// - /// Combine the primary fonts with the fallback fonts to get a font list + /// Gets the fallback fonts with the given size. /// + /// The size. + /// The generated fonts. API_FUNCTION() FORCE_INLINE Array& GetFontList(float size) { Array* result; if (_cache.TryGet(size, result)) { @@ -61,6 +80,8 @@ public: /// Gets the index of the fallback font that should be used to render the char /// /// The char. + /// The primary font. + /// The number to return if none of the fonts can render. /// -1 if char can be rendered with primary font, index if it matches a fallback font. API_FUNCTION() FORCE_INLINE int32 GetCharFallbackIndex(Char c, Font* primaryFont = nullptr, int32 missing = -1) { if (primaryFont && primaryFont->GetAsset()->ContainsChar(c)) { @@ -81,6 +102,10 @@ public: return missing; } + /// + /// Checks if every font is properly loaded. + /// + /// True if every font asset is non-null, otherwise false. API_FUNCTION() FORCE_INLINE bool Verify() { for (int32 i = 0; i < _fontAssets.Count(); i++) { diff --git a/Source/Engine/Render2D/FallbackTextUtils.cs b/Source/Engine/Render2D/FallbackTextUtils.cs index b6d8770e2..486a354bf 100644 --- a/Source/Engine/Render2D/FallbackTextUtils.cs +++ b/Source/Engine/Render2D/FallbackTextUtils.cs @@ -227,5 +227,42 @@ namespace FlaxEngine return font.GetCharPosition(text, ref textRange, index, ref layout); } } + + /// + /// Gets the max font height among the font and all fallback fonts of the same size. + /// + /// The primary font to use. + /// The fallback fonts. + /// The max height. + public static float GetMaxHeight(Font font, FallbackFonts fallbacks) + { + float height = font.Height; + + var fallbackFonts = fallbacks.GetFontList(font.Size); + foreach (var item in fallbackFonts) + { + height = Mathf.Max(height, item.Height); + } + + return height; + } + + /// + /// Gets the max font height among the font and all fallback fonts of the same size. + /// + /// The primary font to use. + /// Whether to enable fallback fonts, uses if true. + /// The max height. + public static float GetMaxHeight(Font font, bool useFallback = true) + { + if(Fallbacks != null && useFallback) + { + return GetMaxHeight(font, Fallbacks); + } + else + { + return font.Height; + } + } } } diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index d932888fb..110a8fe70 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -122,7 +122,8 @@ struct TIsPODType }; /// -/// The font block info generated during text processing. +/// The font block info generated during text processing. +/// A block means a range of text that belongs to the same line and can be rendered with the same font. /// API_STRUCT(NoDefault) struct FontBlockCache { @@ -134,7 +135,7 @@ API_STRUCT(NoDefault) struct FontBlockCache API_FIELD() Float2 Location; /// - /// The height of the current block + /// The size of the current block /// API_FIELD() Float2 Size; @@ -183,7 +184,7 @@ API_STRUCT(NoDefault) struct BlockedTextLineCache API_FIELD() float MaxAscender; /// - /// The index of the font to render with + /// The blocks that belongs to this line /// API_FIELD() Array Blocks; }; diff --git a/Source/Engine/Render2D/FontAsset.cpp b/Source/Engine/Render2D/FontAsset.cpp index 1e94b19b1..ea629367f 100644 --- a/Source/Engine/Render2D/FontAsset.cpp +++ b/Source/Engine/Render2D/FontAsset.cpp @@ -199,6 +199,13 @@ bool FontAsset::Save(const StringView& path) #endif + +/// +/// Check if the font contains the glyph of a char +/// +/// The char to test. +/// True if the font contains the glyph of the char, otherwise false. + bool FontAsset::ContainsChar(Char c) const { return FT_Get_Char_Index(GetFTFace(), c) > 0; } diff --git a/Source/Engine/Render2D/FontAsset.h b/Source/Engine/Render2D/FontAsset.h index a773a8ad6..f3c909911 100644 --- a/Source/Engine/Render2D/FontAsset.h +++ b/Source/Engine/Render2D/FontAsset.h @@ -179,7 +179,7 @@ public: /// /// The char to test. /// True if the font contains the glyph of the char, otherwise false. - API_FUNCTION() FORCE_INLINE bool ContainsChar(Char c) const; + API_FUNCTION() bool ContainsChar(Char c) const; /// /// Invalidates all cached dynamic font atlases using this font. Can be used to reload font characters after changing font asset options. diff --git a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs index a30d8e603..8d7858af0 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs @@ -316,6 +316,7 @@ namespace FlaxEngine.GUI color = textBlock.Style.ShadowColor; if (!enabled) color *= 0.6f; + // We don't need font fallbacks for rich text since the font is user-selected Render2D.DrawText(font, _text, ref textBlock.Range, color, textBlock.Bounds.Location + textBlock.Style.ShadowOffset, textBlock.Style.CustomMaterial); } diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index 70a153faf..b1861df56 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -117,7 +117,7 @@ namespace FlaxEngine.GUI return Float2.Zero; } - height = font.Height / DpiScale; + height = FallbackTextUtils.GetMaxHeight(font) / DpiScale; return FallbackTextUtils.GetCharPosition(font, _text, index, ref _layout); } @@ -171,7 +171,7 @@ namespace FlaxEngine.GUI { var leftEdge = FallbackTextUtils.GetCharPosition(font, _text, SelectionLeft, ref _layout); var rightEdge = FallbackTextUtils.GetCharPosition(font, _text, SelectionRight, ref _layout); - float fontHeight = font.Height / DpiScale; + float fontHeight = FallbackTextUtils.GetMaxHeight(font) / DpiScale; // Draw selection background float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f);