From 47a25c7828cb6351f660ca2a6d282957458b8b6d Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Tue, 28 Nov 2023 07:17:46 +0800 Subject: [PATCH 01/16] Add font fallback Note: All the `First()` in the code are temperary workarounds to make it work and require refractoring --- Source/Editor/Content/Tree/ContentTreeNode.cs | 5 +- .../CustomEditors/Dedicated/ScriptsEditor.cs | 3 +- .../Dedicated/UIControlEditor.cs | 4 +- .../Editors/ActorTransformEditor.cs | 3 +- .../Editor/CustomEditors/Editors/TagEditor.cs | 2 +- Source/Editor/EditorAssets.cs | 2 + Source/Editor/GUI/ComboBox.cs | 2 +- .../GUI/ContextMenu/ContextMenuButton.cs | 7 +- Source/Editor/GUI/CurveEditor.cs | 2 +- Source/Editor/GUI/Docking/DockWindow.cs | 3 +- Source/Editor/GUI/ItemsListContextMenu.cs | 5 +- Source/Editor/GUI/MainMenuButton.cs | 5 +- Source/Editor/GUI/NavigationButton.cs | 5 +- Source/Editor/GUI/Row.cs | 5 +- Source/Editor/GUI/Table.cs | 3 +- .../Editor/GUI/Timeline/Tracks/MemberTrack.cs | 2 +- Source/Editor/GUI/ToolStripButton.cs | 5 +- Source/Editor/GUI/Tree/TreeNode.cs | 3 +- Source/Editor/Options/InterfaceOptions.cs | 6 + Source/Editor/Options/OptionsModule.cs | 9 +- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 4 +- .../Editor/Surface/Archetypes/BehaviorTree.cs | 3 +- .../Surface/ContextMenu/VisjectCMItem.cs | 14 +- Source/Editor/Surface/Elements/InputBox.cs | 4 +- Source/Editor/Surface/SurfaceNode.cs | 5 +- Source/Editor/Tools/Foliage/FoliageTab.cs | 3 +- Source/Editor/Tools/Terrain/CarveTab.cs | 3 +- Source/Editor/Viewport/EditorViewport.cs | 6 +- .../Viewport/Widgets/ViewportWidgetButton.cs | 5 +- Source/Editor/Windows/AboutDialog.cs | 3 +- Source/Editor/Windows/Profiler/Timeline.cs | 5 +- Source/Editor/Windows/ToolboxWindow.cs | 5 +- Source/Engine/Render2D/Font.cpp | 205 +++++++++++ Source/Engine/Render2D/Font.h | 35 ++ Source/Engine/Render2D/FontManager.cpp | 3 + Source/Engine/Render2D/Render2D.cpp | 332 +++++++++++++++++- Source/Engine/Render2D/Render2D.cs | 53 +++ Source/Engine/Render2D/Render2D.h | 45 ++- Source/Engine/Scripting/Scripting.cs | 5 +- Source/Engine/UI/GUI/Common/Button.cs | 3 +- Source/Engine/UI/GUI/Common/Dropdown.cs | 3 +- Source/Engine/UI/GUI/Common/Label.cs | 8 +- Source/Engine/UI/GUI/Common/RichTextBox.cs | 3 +- Source/Engine/UI/GUI/Common/TextBox.cs | 4 +- Source/Engine/UI/GUI/Panels/DropPanel.cs | 3 +- Source/Engine/UI/GUI/Style.cs | 26 +- Source/Engine/UI/GUI/Tooltip.cs | 5 +- 47 files changed, 787 insertions(+), 87 deletions(-) diff --git a/Source/Editor/Content/Tree/ContentTreeNode.cs b/Source/Editor/Content/Tree/ContentTreeNode.cs index 0c0fc6a51..47159c6c9 100644 --- a/Source/Editor/Content/Tree/ContentTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentTreeNode.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System.Collections.Generic; +using System.Linq; using FlaxEditor.GUI; using FlaxEditor.GUI.Drag; using FlaxEditor.GUI.Tree; @@ -140,8 +141,8 @@ namespace FlaxEditor.Content var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(text, ranges[i].StartIndex); - var end = font.GetCharPosition(text, ranges[i].EndIndex); + var start = font.First().GetCharPosition(text, ranges[i].StartIndex); + var end = font.First().GetCharPosition(text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index d7bfbbad7..1f69074ce 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -3,6 +3,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using FlaxEditor.Actions; using FlaxEditor.Content; using FlaxEditor.GUI; @@ -42,7 +43,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Add script button var buttonText = "Add script"; - var textSize = Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Style.Current.FontMedium.First().MeasureText(buttonText); float addScriptButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var buttonHeight = (textSize.Y < 18) ? 18 : textSize.Y + 4; _addScriptsButton = new Button diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index 296560507..5b5c2f90d 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -247,7 +247,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Info var info = new Label(0, title.Bottom, DialogWidth, InfoHeight) { - Font = new FontReference(style.FontSmall), + Font = new FontReference(style.FontSmall.First()), Text = "Shift: also set bounds\nControl: also set pivot", Parent = this }; @@ -423,7 +423,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Set control type button var space = layout.Space(20); var buttonText = "Set Type"; - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.First().MeasureText(buttonText); float setTypeButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var setTypeButton = new Button { diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index 4c153e759..d7059f712 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -2,6 +2,7 @@ using FlaxEngine; using FlaxEngine.GUI; +using System.Linq; namespace FlaxEditor.CustomEditors.Editors { @@ -100,7 +101,7 @@ namespace FlaxEditor.CustomEditors.Editors _linkButton.Clicked += ToggleLink; ToggleEnabled(); SetLinkStyle(); - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(LinkedLabel.Text.Value); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.First().MeasureText(LinkedLabel.Text.Value); _linkButton.LocalX += textSize.X + 10; LinkedLabel.SetupContextMenu += (label, menu, editor) => { diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs index dbd5d124c..a174d02e4 100644 --- a/Source/Editor/CustomEditors/Editors/TagEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs @@ -631,7 +631,7 @@ namespace FlaxEditor.CustomEditors.Editors TooltipText = "Edit...", Parent = _label, }; - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.First().MeasureText(buttonText); if (textSize.Y > button.Width) button.Width = textSize.Y + 2; diff --git a/Source/Editor/EditorAssets.cs b/Source/Editor/EditorAssets.cs index eb2f21356..237908a0c 100644 --- a/Source/Editor/EditorAssets.cs +++ b/Source/Editor/EditorAssets.cs @@ -54,6 +54,8 @@ namespace FlaxEditor /// public static string PrimaryFont = "Editor/Fonts/Roboto-Regular"; + public static string CJKFont = "Editor/Fonts/NotoSansSC-Medium"; + /// /// The Inconsolata Regular font. /// diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index 0417cc7e3..7dc407698 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -273,7 +273,7 @@ namespace FlaxEditor.GUI MaximumItemsInViewCount = 20; var style = Style.Current; - Font = new FontReference(style.FontMedium); + Font = new FontReference(style.FontMedium.First()); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BackgroundColorHighlighted = BackgroundColor; diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index e371f7c4b..872700a9b 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -233,11 +234,11 @@ namespace FlaxEditor.GUI.ContextMenu { var style = Style.Current; float width = 20; - if (style.FontMedium) + if (style.FontMedium.First()) { - width += style.FontMedium.MeasureText(Text).X; + width += style.FontMedium.First().MeasureText(Text).X; if (!string.IsNullOrEmpty(ShortKeys)) - width += 40 + style.FontMedium.MeasureText(ShortKeys).X; + width += 40 + style.FontMedium.First().MeasureText(ShortKeys).X; } return Mathf.Max(width, base.MinimumWidth); diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index 22deec120..f71409b20 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -437,7 +437,7 @@ namespace FlaxEditor.GUI _contentsColor = style.Background.RGBMultiplied(0.7f); _linesColor = style.ForegroundDisabled.RGBMultiplied(0.7f); _labelsColor = style.ForegroundDisabled; - _labelsFont = style.FontSmall; + _labelsFont = style.FontSmall.First(); _mainPanel = new Panel(ScrollBars.Both) { diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index dd4e39ce2..8601b9c53 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -6,6 +6,7 @@ using FlaxEngine; using FlaxEngine.Assertions; using FlaxEngine.GUI; using FlaxEditor.Options; +using System.Linq; namespace FlaxEditor.GUI.Docking { @@ -488,7 +489,7 @@ namespace FlaxEditor.GUI.Docking { var style = Style.Current; if (style?.FontMedium != null) - _titleSize = style.FontMedium.MeasureText(_title); + _titleSize = style.FontMedium.First().MeasureText(_title); } base.PerformLayoutBeforeChildren(); diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index 42d236991..ce69f3544 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Input; using FlaxEditor.Utilities; @@ -86,8 +87,8 @@ namespace FlaxEditor.GUI var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(Name, ranges[i].StartIndex); - var end = font.GetCharPosition(Name, ranges[i].EndIndex); + var start = font.First().GetCharPosition(Name, ranges[i].StartIndex); + var end = font.First().GetCharPosition(Name, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + 2, 0, end.X - start.X, Height)); } Visible = true; diff --git a/Source/Editor/GUI/MainMenuButton.cs b/Source/Editor/GUI/MainMenuButton.cs index 117922361..76687f8b9 100644 --- a/Source/Editor/GUI/MainMenuButton.cs +++ b/Source/Editor/GUI/MainMenuButton.cs @@ -2,6 +2,7 @@ using FlaxEngine; using FlaxEngine.GUI; +using System.Linq; namespace FlaxEditor.GUI { @@ -101,8 +102,8 @@ namespace FlaxEditor.GUI var style = Style.Current; float width = 18; - if (style.FontMedium) - width += style.FontMedium.MeasureText(Text).X; + if (style.FontMedium.First()) + width += style.FontMedium.First().MeasureText(Text).X; Width = width; } diff --git a/Source/Editor/GUI/NavigationButton.cs b/Source/Editor/GUI/NavigationButton.cs index 18e862304..77f8a7656 100644 --- a/Source/Editor/GUI/NavigationButton.cs +++ b/Source/Editor/GUI/NavigationButton.cs @@ -2,6 +2,7 @@ using FlaxEngine; using FlaxEngine.GUI; +using System.Linq; namespace FlaxEditor.GUI { @@ -65,9 +66,9 @@ namespace FlaxEditor.GUI { var style = Style.Current; - if (style.FontMedium) + if (style.FontMedium.First()) { - Width = style.FontMedium.MeasureText(Text).X + 2 * DefaultMargin; + Width = style.FontMedium.First().MeasureText(Text).X + 2 * DefaultMargin; } } } diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index f6bd5b02a..458138aea 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -38,8 +39,8 @@ namespace FlaxEditor.GUI { Depth = -1; - if (Height < Style.Current.FontMedium.Height) - Height = Style.Current.FontMedium.Height + 4; + if (Height < Style.Current.FontMedium.First().Height) + Height = Style.Current.FontMedium.First().Height + 4; } /// diff --git a/Source/Editor/GUI/Table.cs b/Source/Editor/GUI/Table.cs index 1d22ddb15..fed33a336 100644 --- a/Source/Editor/GUI/Table.cs +++ b/Source/Editor/GUI/Table.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; using System.Runtime.CompilerServices; using FlaxEngine; using FlaxEngine.GUI; @@ -129,7 +130,7 @@ namespace FlaxEditor.GUI Render2D.FillRectangle(rect, column.TitleBackgroundColor); var style = Style.Current; - var font = column.TitleFont ?? style.FontMedium; + var font = column.TitleFont ?? style.FontMedium.First(); Render2D.DrawText(font, column.Title, rect, column.TitleColor, TextAlignment.Center, TextAlignment.Center); if (columnIndex < _columns.Length - 1) diff --git a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs index 63787df2c..928129917 100644 --- a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs @@ -345,7 +345,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks if (_previewValue != null) { // Based on Track.Draw for track text placement - var left = _xOffset + 16 + Style.Current.FontSmall.MeasureText(Title ?? Name).X; + var left = _xOffset + 16 + Style.Current.FontSmall.First().MeasureText(Title ?? Name).X; if (Icon.IsValid) left += 18; if (IsExpanded) diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index e839a7356..b74c7c19f 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -150,8 +151,8 @@ namespace FlaxEditor.GUI if (hasSprite) width += iconSize; - if (!string.IsNullOrEmpty(_text) && style.FontMedium) - width += style.FontMedium.MeasureText(_text).X + (hasSprite ? DefaultMargin : 0); + if (!string.IsNullOrEmpty(_text) && style.FontMedium.First()) + width += style.FontMedium.First().MeasureText(_text).X + (hasSprite ? DefaultMargin : 0); Width = width; } diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 703079469..f26929993 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -317,7 +318,7 @@ namespace FlaxEditor.GUI.Tree BackgroundColorSelected = style.BackgroundSelected; BackgroundColorHighlighted = style.BackgroundHighlighted; BackgroundColorSelectedUnfocused = style.LightBackground; - TextFont = new FontReference(style.FontSmall); + TextFont = new FontReference(style.FontSmall.First()); } /// diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 95a273f19..c96a52fb1 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -237,12 +237,18 @@ namespace FlaxEditor.Options public int NumberOfGameClientsToLaunch = 1; private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); + private static FontAsset _cjkFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CJKFont); private FontReference _titleFont = new FontReference(DefaultFont, 18); private FontReference _largeFont = new FontReference(DefaultFont, 14); private FontReference _mediumFont = new FontReference(DefaultFont, 9); private FontReference _smallFont = new FontReference(DefaultFont, 9); private FontReference _outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont), 10); + public FontReference CJKFont + { + get => new FontReference(_cjkFont, 9); + } + /// /// Gets or sets the title font for editor UI. /// diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index 1137e4c37..15059bd56 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -261,8 +261,9 @@ namespace FlaxEditor.Options // Fonts FontTitle = options.Interface.TitleFont.GetFont(), FontLarge = options.Interface.LargeFont.GetFont(), - FontMedium = options.Interface.MediumFont.GetFont(), - FontSmall = options.Interface.SmallFont.GetFont(), + FontMedium = new Font[] { options.Interface.MediumFont.GetFont(), options.Interface.CJKFont.GetFont() }, + FontSmall = new Font[] { options.Interface.SmallFont.GetFont(), options.Interface.CJKFont.GetFont() }, + FontCJK = options.Interface.CJKFont.GetFont(), // Icons ArrowDown = Editor.Icons.ArrowDown12, @@ -314,8 +315,8 @@ namespace FlaxEditor.Options // Fonts FontTitle = options.Interface.TitleFont.GetFont(), FontLarge = options.Interface.LargeFont.GetFont(), - FontMedium = options.Interface.MediumFont.GetFont(), - FontSmall = options.Interface.SmallFont.GetFont(), + FontMedium = new Font[] { options.Interface.MediumFont.GetFont(), options.Interface.CJKFont.GetFont() }, + FontSmall = new Font[] { options.Interface.SmallFont.GetFont(), options.Interface.CJKFont.GetFont() }, // Icons ArrowDown = Editor.Icons.ArrowDown12, diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index f64e46385..9a6fe4fd2 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -142,8 +142,8 @@ namespace FlaxEditor.SceneGraph.GUI var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(text, ranges[i].StartIndex); - var end = font.GetCharPosition(text, ranges[i].EndIndex); + var start = font.First().GetCharPosition(text, ranges[i].StartIndex); + var end = font.First().GetCharPosition(text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/Surface/Archetypes/BehaviorTree.cs b/Source/Editor/Surface/Archetypes/BehaviorTree.cs index f8cd7bb5a..38dd28e62 100644 --- a/Source/Editor/Surface/Archetypes/BehaviorTree.cs +++ b/Source/Editor/Surface/Archetypes/BehaviorTree.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.CustomEditors.Dedicated; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Drag; @@ -100,7 +101,7 @@ namespace FlaxEditor.Surface.Archetypes _debugRelevant = Behavior.GetNodeDebugRelevancy(instance, behavior); _debugInfo = Behavior.GetNodeDebugInfo(instance, behavior); if (!string.IsNullOrEmpty(_debugInfo)) - _debugInfoSize = Style.Current.FontSmall.MeasureText(_debugInfo); + _debugInfoSize = Style.Current.FontSmall.First().MeasureText(_debugInfo); } } diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 207875a92..812bec5d2 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -200,8 +200,8 @@ namespace FlaxEditor.Surface.ContextMenu var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(_archetype.Title, ranges[i].StartIndex); - var end = font.GetCharPosition(_archetype.Title, ranges[i].EndIndex); + var start = font.First().GetCharPosition(_archetype.Title, ranges[i].StartIndex); + var end = font.First().GetCharPosition(_archetype.Title, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); if (ranges[i].StartIndex <= 0) @@ -222,8 +222,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = font.GetCharPosition(_archetype.Title, 0); - var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); + var start = font.First().GetCharPosition(_archetype.Title, 0); + var end = font.First().GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); _isFullMatch = true; Visible = true; @@ -237,8 +237,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = font.GetCharPosition(_archetype.Title, 0); - var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); + var start = font.First().GetCharPosition(_archetype.Title, 0); + var end = font.First().GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); Visible = true; @@ -286,7 +286,7 @@ namespace FlaxEditor.Surface.ContextMenu Render2D.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); if (_archetype.SubTitle != null) { - var titleLength = style.FontSmall.MeasureText(_archetype.Title).X; + var titleLength = style.FontSmall.First().MeasureText(_archetype.Title).X; var subTitleRect = new Rectangle(textRect.X + titleLength, textRect.Y, textRect.Width - titleLength, textRect.Height); Render2D.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } diff --git a/Source/Editor/Surface/Elements/InputBox.cs b/Source/Editor/Surface/Elements/InputBox.cs index 2047bcd1a..611160611 100644 --- a/Source/Editor/Surface/Elements/InputBox.cs +++ b/Source/Editor/Surface/Elements/InputBox.cs @@ -1428,7 +1428,7 @@ namespace FlaxEditor.Surface.Elements if (_defaultValueEditor != null) { - _defaultValueEditor.Location = new Float2(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y); + _defaultValueEditor.Location = new Float2(X + Width + 8 + Style.Current.FontSmall.First().MeasureText(Text).X, Y); } } @@ -1635,7 +1635,7 @@ namespace FlaxEditor.Surface.Elements { if (DefaultValueEditors[i].CanUse(this, ref _currentType)) { - var bounds = new Rectangle(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y, 90, Height); + var bounds = new Rectangle(X + Width + 8 + Style.Current.FontSmall.First().MeasureText(Text).X, Y, 90, Height); _editor = DefaultValueEditors[i]; try { diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 780ef81f0..82a9ab2bd 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.Scripting; using FlaxEditor.Surface.Elements; using FlaxEditor.Surface.Undo; @@ -199,7 +200,7 @@ namespace FlaxEditor.Surface continue; if (child is InputBox inputBox) { - var boxWidth = boxLabelFont.MeasureText(inputBox.Text).X + 20; + var boxWidth = boxLabelFont.First().MeasureText(inputBox.Text).X + 20; if (inputBox.DefaultValueEditor != null) boxWidth += inputBox.DefaultValueEditor.Width + 4; leftWidth = Mathf.Max(leftWidth, boxWidth); @@ -207,7 +208,7 @@ namespace FlaxEditor.Surface } else if (child is OutputBox outputBox) { - rightWidth = Mathf.Max(rightWidth, boxLabelFont.MeasureText(outputBox.Text).X + 20); + rightWidth = Mathf.Max(rightWidth, boxLabelFont.First().MeasureText(outputBox.Text).X + 20); rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } else if (child is Control control) diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index 1b46a42be..e88972b5c 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.GUI.Tabs; using FlaxEditor.Modules; using FlaxEditor.SceneGraph.Actors; @@ -147,7 +148,7 @@ namespace FlaxEditor.Tools.Foliage Parent = _noFoliagePanel, Enabled = false }; - var textSize = Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Style.Current.FontMedium.First().MeasureText(buttonText); if (_createNewFoliage.Width < textSize.X) { _createNewFoliage.LocalX -= (textSize.X - _createNewFoliage.Width) / 2; diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index d51915acf..0a43cd2d2 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEditor.GUI.Tabs; using FlaxEditor.Modules; using FlaxEditor.SceneGraph.Actors; @@ -105,7 +106,7 @@ namespace FlaxEditor.Tools.Terrain Parent = _noTerrainPanel, Enabled = false }; - var textSize = Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Style.Current.FontMedium.First().MeasureText(buttonText); if (_createTerrainButton.Width < textSize.X) { _createTerrainButton.LocalX -= (textSize.X - _createTerrainButton.Width) / 2; diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index c49392d01..f4ebfb51b 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -548,9 +548,9 @@ namespace FlaxEditor.Viewport #region Camera settings widget var largestText = "Relative Panning"; - var textSize = Style.Current.FontMedium.MeasureText(largestText); + var textSize = Style.Current.FontMedium.First().MeasureText(largestText); var xLocationForExtras = textSize.X + 5; - var cameraSpeedTextWidth = Style.Current.FontMedium.MeasureText("0.00").X; + var cameraSpeedTextWidth = Style.Current.FontMedium.First().MeasureText("0.00").X; // Camera Settings Widget _cameraWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); @@ -801,7 +801,7 @@ namespace FlaxEditor.Viewport #region View mode widget largestText = "Brightness"; - textSize = Style.Current.FontMedium.MeasureText(largestText); + textSize = Style.Current.FontMedium.First().MeasureText(largestText); xLocationForExtras = textSize.X + 5; var viewMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft); diff --git a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs index 77e94ae53..a58350ded 100644 --- a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs +++ b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEditor.GUI.ContextMenu; using FlaxEngine; using FlaxEngine.GUI; @@ -162,8 +163,8 @@ namespace FlaxEditor.Viewport.Widgets { var style = Style.Current; - if (style != null && style.FontMedium) - Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.MeasureText(_text).X, Icon.IsValid); + if (style != null && style.FontMedium.First()) + Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.First().MeasureText(_text).X, Icon.IsValid); } } } diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index b059dadcb..63ad44f29 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -2,6 +2,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System.Collections.Generic; +using System.Linq; using FlaxEditor.GUI.Dialogs; using FlaxEngine; using FlaxEngine.GUI; @@ -53,7 +54,7 @@ namespace FlaxEditor.Windows Parent = this }; var buttonText = "Copy version info"; - var fontSize = Style.Current.FontMedium.MeasureText(buttonText); + var fontSize = Style.Current.FontMedium.First().MeasureText(buttonText); var copyVersionButton = new Button(Width - fontSize.X - 8, 6, fontSize.X + 4, 20) { Text = buttonText, diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index 59a7a0e26..88019b329 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -2,6 +2,7 @@ using FlaxEngine; using FlaxEngine.GUI; +using System.Linq; namespace FlaxEditor.Windows.Profiler { @@ -84,8 +85,8 @@ namespace FlaxEditor.Windows.Profiler Render2D.FillRectangle(bounds, color); Render2D.DrawRectangle(bounds, color * 0.5f); - if (_nameLength < 0 && style.FontMedium) - _nameLength = style.FontMedium.MeasureText(_name).X; + if (_nameLength < 0 && style.FontMedium.First()) + _nameLength = style.FontMedium.First().MeasureText(_name).X; if (_nameLength < bounds.Width + 4) { diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs index e2b04669b..e8fc1d56c 100644 --- a/Source/Editor/Windows/ToolboxWindow.cs +++ b/Source/Editor/Windows/ToolboxWindow.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.GUI.Input; using FlaxEditor.GUI.Tabs; using FlaxEditor.GUI.Tree; @@ -272,8 +273,8 @@ namespace FlaxEditor.Windows var textRect = item.TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(text, ranges[i].StartIndex); - var end = font.GetCharPosition(text, ranges[i].EndIndex); + var start = font.First().GetCharPosition(text, ranges[i].StartIndex); + var end = font.First().GetCharPosition(text, ranges[i].EndIndex); highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } item.SetHighlights(highlights); diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index 90423f5ce..13b17197c 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -283,6 +283,206 @@ void Font::ProcessText(const StringView& text, Array& outputLines } } +void Font::ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) +{ + float cursorX = 0; + int32 kerning; + FontLineCache tmpLine; + FontCharacterEntry entry; + FontCharacterEntry previous; + int32 textLength = text.Length(); + float scale = layout.Scale / FontManager::FontScale; + float boundsWidth = layout.Bounds.GetWidth(); + float baseLinesDistanceScale = layout.BaseLinesGapScale * scale; + tmpLine.Location = Float2::Zero; + tmpLine.Size = Float2::Zero; + tmpLine.FirstCharIndex = 0; + tmpLine.LastCharIndex = -1; + + int32 lastWrapCharIndex = INVALID_INDEX; + float lastWrapCharX = 0; + bool lastMoveLine = false; + + int32 previousFontIndex = -1; + // The maximum font height of the current line + float maxHeight = 0; + // Process each character to split text into single lines + for (int32 currentIndex = 0; currentIndex < textLength;) + { + bool moveLine = false; + float xAdvance = 0; + int32 nextCharIndex = currentIndex + 1; + + // Cache current character + const Char currentChar = text[currentIndex]; + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); + + // Check if character can wrap words + const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar); + if (isWrapChar && currentIndex != 0) + { + lastWrapCharIndex = currentIndex; + lastWrapCharX = cursorX; + } + + // Check if it's a newline character + if (currentChar == '\n') + { + // Break line + moveLine = true; + currentIndex++; + tmpLine.LastCharIndex++; + } + else + { + // Get character entry + int32 fontIndex = 0; + while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(currentChar)) + { + fontIndex++; + } + + // If no font can match the char, then use the first font + if (fontIndex == fonts.Count()) { + fontIndex = 0; + } + // Get character entry + fonts[fontIndex]->GetCharacter(currentChar, entry); + maxHeight = Math::Max(maxHeight, static_cast(fonts[fontIndex]->GetHeight())); + + // Get kerning, only when the font hasn't changed + if (!isWhitespace && previous.IsValid && previousFontIndex == fontIndex) + { + kerning = fonts[fontIndex]->GetKerning(previous.Character, entry.Character); + } + else + { + kerning = 0; + } + previous = entry; + previousFontIndex = fontIndex; + xAdvance = (kerning + entry.AdvanceX) * scale; + + // Check if character fits the line or skip wrapping + if (cursorX + xAdvance <= boundsWidth || layout.TextWrapping == TextWrapping::NoWrap) + { + // Move character + cursorX += xAdvance; + tmpLine.LastCharIndex++; + } + else if (layout.TextWrapping == TextWrapping::WrapWords) + { + if (lastWrapCharIndex != INVALID_INDEX) + { + // Skip moving twice for the same character + int32 lastLineLasCharIndex = outputLines.HasItems() ? outputLines.Last().LastCharIndex : -10000; + if (lastLineLasCharIndex == lastWrapCharIndex || lastLineLasCharIndex == lastWrapCharIndex - 1 || lastLineLasCharIndex == lastWrapCharIndex - 2) + { + currentIndex = nextCharIndex; + lastMoveLine = moveLine; + continue; + } + + // Move line + const Char wrapChar = text[lastWrapCharIndex]; + moveLine = true; + cursorX = lastWrapCharX; + if (StringUtils::IsWhitespace(wrapChar)) + { + // Skip whitespaces + tmpLine.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex + 1; + } + else + { + tmpLine.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex; + } + } + } + else if (layout.TextWrapping == TextWrapping::WrapChars) + { + // Move line + moveLine = true; + nextCharIndex = currentIndex; + + // Skip moving twice for the same character + if (lastMoveLine) + break; + } + } + + // Check if move to another line + if (moveLine) + { + // Add line + tmpLine.Size.X = cursorX; + tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; + tmpLine.LastCharIndex = Math::Max(tmpLine.LastCharIndex, tmpLine.FirstCharIndex); + outputLines.Add(tmpLine); + + // Reset line + tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; + tmpLine.FirstCharIndex = currentIndex; + tmpLine.LastCharIndex = currentIndex - 1; + cursorX = 0; + lastWrapCharIndex = INVALID_INDEX; + lastWrapCharX = 0; + previous.IsValid = false; + + // Reset max font height + maxHeight = 0; + } + + currentIndex = nextCharIndex; + lastMoveLine = moveLine; + } + + if (textLength != 0 && (tmpLine.LastCharIndex >= tmpLine.FirstCharIndex || text[textLength - 1] == '\n')) + { + // Add line + tmpLine.Size.X = cursorX; + tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; + tmpLine.LastCharIndex = textLength - 1; + outputLines.Add(tmpLine); + + tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; + } + + // Check amount of lines + if (outputLines.IsEmpty()) + return; + + float totalHeight = tmpLine.Location.Y; + + Float2 offset = Float2::Zero; + if (layout.VerticalAlignment == TextAlignment::Center) + { + offset.Y += (layout.Bounds.GetHeight() - totalHeight) * 0.5f; + } + else if (layout.VerticalAlignment == TextAlignment::Far) + { + offset.Y += layout.Bounds.GetHeight() - totalHeight; + } + for (int32 i = 0; i < outputLines.Count(); i++) + { + FontLineCache& line = outputLines[i]; + Float2 rootPos = line.Location + offset; + + // Fix upper left line corner to match desire text alignment + if (layout.HorizontalAlignment == TextAlignment::Center) + { + rootPos.X += (layout.Bounds.GetWidth() - line.Size.X) * 0.5f; + } + else if (layout.HorizontalAlignment == TextAlignment::Far) + { + rootPos.X += layout.Bounds.GetWidth() - line.Size.X; + } + + line.Location = rootPos; + } +} + Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout) { // Check if there is no need to do anything @@ -432,6 +632,11 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo return rootOffset + Float2(lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); } +bool Font::ContainsChar(Char c) +{ + return FT_Get_Char_Index(GetAsset()->GetFTFace(), c) > 0; +} + void Font::FlushFaceSize() const { // Set the character size diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index 90f723cd8..d8ac1b708 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -339,6 +339,15 @@ public: /// The output lines list. void ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); + /// + /// Processes text to get cached lines for rendering. + /// + /// The font list. + /// The input text. + /// The layout properties. + /// The output lines list. + static void ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); + /// /// Processes text to get cached lines for rendering. /// @@ -395,6 +404,15 @@ public: /// The minimum size for that text and fot to render properly. API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + /// + /// Measures minimum size of the rectangle that will be needed to draw given text. + /// + /// The fonts to render with. + /// The input text to test. + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() static Float2 MeasureText(const Array& fonts, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + /// /// Measures minimum size of the rectangle that will be needed to draw given text. /// @@ -482,6 +500,16 @@ public: /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + /// + /// Calculates character position for given text and character index. + /// + /// The fonts to use. + /// The input text to test. + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() static Float2 GetCharPosition(const Array& fonts, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + /// /// Calculates character position for given text and character index. /// @@ -518,6 +546,13 @@ public: return GetCharPosition(textRange.Substring(text), index, TextLayoutOptions()); } + /// + /// 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. + API_FUNCTION() FORCE_INLINE bool ContainsChar(Char c); + /// /// Flushes the size of the face with the Free Type library backend. /// diff --git a/Source/Engine/Render2D/FontManager.cpp b/Source/Engine/Render2D/FontManager.cpp index bb7edf974..5b08c1d87 100644 --- a/Source/Engine/Render2D/FontManager.cpp +++ b/Source/Engine/Render2D/FontManager.cpp @@ -155,6 +155,9 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry) // Get the index to the glyph in the font face const FT_UInt glyphIndex = FT_Get_Char_Index(face, c); + if (glyphIndex == 0) { + LOG(Warning, "Font `{}` doesn't contain character `\\u{:x}`, consider choosing another font. ", String(face->family_name), c); + } // Load the glyph const FT_Error error = FT_Load_Glyph(face, glyphIndex, glyphFlags); diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index b56c3e1a2..eedfbeffe 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -27,11 +27,11 @@ #if USE_EDITOR #define RENDER2D_CHECK_RENDERING_STATE \ - if (!Render2D::IsRendering()) \ - { \ - LOG(Error, "Calling Render2D is only valid during rendering."); \ - return; \ - } + if (!Render2D::IsRendering()) \ + { \ + LOG(Error, "Calling Render2D is only valid during rendering."); \ + return; \ + } #else #define RENDER2D_CHECK_RENDERING_STATE #endif @@ -54,7 +54,7 @@ const bool DownsampleForBlur = false; PACK_STRUCT(struct Data { Matrix ViewProjection; - }); +}); PACK_STRUCT(struct BlurData { Float2 InvBufferSize; @@ -62,7 +62,7 @@ PACK_STRUCT(struct BlurData { float Dummy0; Float4 Bounds; Float4 WeightAndOffsets[RENDER2D_BLUR_MAX_SAMPLES / 2]; - }); +}); enum class DrawCallType : byte { @@ -1174,7 +1174,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, drawCall.AsChar.Mat = nullptr; } Float2 pointer = location; - for (int32 currentIndex = 0; currentIndex <= text.Length(); currentIndex++) + for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) { // Cache current character const Char currentChar = text[currentIndex]; @@ -1368,6 +1368,318 @@ void Render2D::DrawText(Font* font, const StringView& text, const TextRange& tex DrawText(font, textRange.Substring(text), color, layout, customMaterial); } +void Render2D::DrawText(const Array& fonts, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) +{ + RENDER2D_CHECK_RENDERING_STATE; + + // Check if there is no need to do anything + if (fonts.IsEmpty() || text.Length() < 0) + return; + + // Temporary data + uint32 fontAtlasIndex = 0; + FontTextureAtlas* fontAtlas = nullptr; + Float2 invAtlasSize = Float2::One; + FontCharacterEntry previous; + int32 kerning; + float scale = 1.0f / FontManager::FontScale; + + // Render all characters + FontCharacterEntry entry; + Render2DDrawCall drawCall; + if (customMaterial) + { + drawCall.Type = DrawCallType::DrawCharMaterial; + drawCall.AsChar.Mat = customMaterial; + } + else + { + drawCall.Type = DrawCallType::DrawChar; + drawCall.AsChar.Mat = nullptr; + } + + // The following code cut the text into segments, according to the font used to render + Float2 pointer = location; + // The starting index of the current segment + int32 startIndex = 0; + // The index of the font used by the current segment + int32 segmentFontIndex = 0; + // The maximum font height of the current line + float maxHeight = 0; + for (int32 currentIndex = 0; currentIndex <= text.Length(); currentIndex++) + { + // Cache current character + const Char currentChar = currentIndex < text.Length() ? text[currentIndex] : 0; + + // Check if it isn't a newline character + if (currentChar != '\n') + { + int32 fontIndex = 0; + if (currentIndex < text.Length()) { + while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(currentChar)) + { + fontIndex++; + } + + // If no font can match the char, then use the segment font + if (fontIndex == fonts.Count()) { + fontIndex = segmentFontIndex; + } + + // Do nothing if the char still belongs to the current segment + if (fontIndex == segmentFontIndex) { + continue; + } + } + + // Render the pending segment before beginning the new segment + renderText:auto fontHeight = fonts[segmentFontIndex]->GetHeight(); + maxHeight = Math::Max(maxHeight, static_cast(fontHeight)); + auto fontDescender = fonts[segmentFontIndex]->GetDescender(); + for (int32 renderIndex = startIndex; renderIndex < currentIndex; renderIndex++) + { + // Get character entry + fonts[segmentFontIndex]->GetCharacter(text[renderIndex], entry); + + // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) + if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) + { + // Get texture atlas that contains current character + fontAtlasIndex = entry.TextureIndex; + fontAtlas = FontManager::GetAtlas(fontAtlasIndex); + if (fontAtlas) + { + fontAtlas->EnsureTextureCreated(); + drawCall.AsChar.Tex = fontAtlas->GetTexture(); + invAtlasSize = 1.0f / fontAtlas->GetSize(); + } + else + { + drawCall.AsChar.Tex = nullptr; + invAtlasSize = 1.0f; + } + } + + // Check if character is a whitespace + const bool isWhitespace = StringUtils::IsWhitespace(text[renderIndex]); + + // Get kerning + if (!isWhitespace && previous.IsValid) + { + kerning = fonts[segmentFontIndex]->GetKerning(previous.Character, entry.Character); + } + else + { + kerning = 0; + } + pointer.X += kerning * scale; + previous = entry; + + // Omit whitespace characters + if (!isWhitespace) + { + // Calculate character size and atlas coordinates + const float x = pointer.X + entry.OffsetX * scale; + const float y = pointer.Y + (fontHeight + fontDescender - entry.OffsetY) * scale; + + Rectangle charRect(x, y, entry.UVSize.X * scale, entry.UVSize.Y * scale); + + Float2 upperLeftUV = entry.UV * invAtlasSize; + Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; + + // Add draw call + drawCall.StartIB = IBIndex; + drawCall.CountIB = 6; + DrawCalls.Add(drawCall); + WriteRect(charRect, color, upperLeftUV, rightBottomUV); + } + + // Move + pointer.X += entry.AdvanceX * scale; + } + + // Start new segment + startIndex = currentIndex; + segmentFontIndex = fontIndex; + + if (currentIndex == text.Length() - 1) { + currentIndex++; + goto renderText; + } + } + else + { + // Move + pointer.X = location.X; + pointer.Y += maxHeight * scale; + // Clear max height + maxHeight = 0; + } + } +} + +void Render2D::DrawText(const Array& fonts, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) +{ + DrawText(fonts, textRange.Substring(text), color, location, customMaterial); +} + +void Render2D::DrawText(const Array& fonts, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +{ + RENDER2D_CHECK_RENDERING_STATE; + + // Check if there is no need to do anything + if (fonts.IsEmpty() || text.IsEmpty() || layout.Scale <= ZeroTolerance) + return; + + // Temporary data + uint32 fontAtlasIndex = 0; + FontTextureAtlas* fontAtlas = nullptr; + Float2 invAtlasSize = Float2::One; + FontCharacterEntry previous; + int32 kerning; + float scale = layout.Scale / FontManager::FontScale; + + // Process text to get lines + Lines.Clear(); + Font::ProcessText(fonts, text, Lines, layout); + + // Render all lines + FontCharacterEntry entry; + Render2DDrawCall drawCall; + if (customMaterial) + { + drawCall.Type = DrawCallType::DrawCharMaterial; + drawCall.AsChar.Mat = customMaterial; + } + else + { + drawCall.Type = DrawCallType::DrawChar; + drawCall.AsChar.Mat = nullptr; + } + + for (int32 lineIndex = 0; lineIndex < Lines.Count(); lineIndex++) + { + const FontLineCache& line = Lines[lineIndex]; + + // The following code cut the text into segments, according to the font used to render + Float2 pointer = line.Location; + // The starting index of the current segment + int32 startIndex = line.FirstCharIndex; + // The index of the font used by the current segment + int32 segmentFontIndex = 0; + // The maximum font height of the current line + float maxHeight = 0; + + // Partition and render all characters from the line + for (int32 charIndex = line.FirstCharIndex; charIndex <= line.LastCharIndex + 1; charIndex++) + { + const Char c = charIndex <= line.LastCharIndex ? text[charIndex] : 0; + + if (c != '\n') + { + int32 fontIndex = 0; + if (charIndex <= line.LastCharIndex) { + while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(c)) + { + fontIndex++; + } + + // If no font can match the char, then use the segment font + if (fontIndex == fonts.Count()) { + fontIndex = segmentFontIndex; + } + + + // Do nothing if the char still belongs to the current segment + if (fontIndex == segmentFontIndex) { + continue; + } + } + + + // Render the pending segment before beginning the new segment + auto fontHeight = fonts[segmentFontIndex]->GetHeight(); + maxHeight = Math::Max(maxHeight, static_cast(fontHeight)); + auto fontDescender = fonts[segmentFontIndex]->GetDescender(); + + const Char* pred = L"Type"; + if (text.Substring(0, Math::Min(4, text.Length())) == pred) { + // __debugbreak(); + } + for (int32 renderIndex = startIndex; renderIndex < charIndex; renderIndex++) + { + // Get character entry + fonts[segmentFontIndex]->GetCharacter(text[renderIndex], entry); + + // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) + if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) + { + // Get texture atlas that contains current character + fontAtlasIndex = entry.TextureIndex; + fontAtlas = FontManager::GetAtlas(fontAtlasIndex); + if (fontAtlas) + { + fontAtlas->EnsureTextureCreated(); + invAtlasSize = 1.0f / fontAtlas->GetSize(); + drawCall.AsChar.Tex = fontAtlas->GetTexture(); + } + else + { + invAtlasSize = 1.0f; + drawCall.AsChar.Tex = nullptr; + } + } + + // Get kerning + const bool isWhitespace = StringUtils::IsWhitespace(text[renderIndex]); + if (!isWhitespace && previous.IsValid) + { + kerning = fonts[segmentFontIndex]->GetKerning(previous.Character, entry.Character); + } + else + { + kerning = 0; + } + pointer.X += (float)kerning * scale; + previous = entry; + + // Omit whitespace characters + if (!isWhitespace) + { + // Calculate character size and atlas coordinates + const float x = pointer.X + entry.OffsetX * scale; + const float y = pointer.Y - entry.OffsetY * scale + Math::Ceil((fontHeight + fontDescender) * scale); + + Rectangle charRect(x, y, entry.UVSize.X * scale, entry.UVSize.Y * scale); + charRect.Offset(layout.Bounds.Location); + + Float2 upperLeftUV = entry.UV * invAtlasSize; + Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; + + // Add draw call + drawCall.StartIB = IBIndex; + drawCall.CountIB = 6; + DrawCalls.Add(drawCall); + WriteRect(charRect, color, upperLeftUV, rightBottomUV); + } + + // Move + pointer.X += entry.AdvanceX * scale; + } + + // Start new segment + startIndex = charIndex; + segmentFontIndex = fontIndex; + } + } + } +} + +void Render2D::DrawText(const Array& fonts, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +{ + DrawText(fonts, textRange.Substring(text), color, layout, customMaterial); +} + FORCE_INLINE bool NeedAlphaWithTint(const Color& color) { return (color.A * TintLayersStack.Peek().A) < 1.0f; @@ -1931,7 +2243,7 @@ void Render2D::DrawBlur(const Rectangle& rect, float blurStrength) void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span& vertices, const Span& uvs) { RENDER2D_CHECK_RENDERING_STATE; - CHECK(vertices.Length() == uvs.Length()) + CHECK(vertices.Length() == uvs.Length()); Render2DDrawCall& drawCall = DrawCalls.AddOne(); drawCall.Type = DrawCallType::FillTexture; @@ -1977,7 +2289,7 @@ void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span& indices, drawCall.StartIB = IBIndex; drawCall.CountIB = indices.Length(); drawCall.AsTexture.Ptr = t; - + for (int32 i = 0; i < indices.Length();) { const uint16 i0 = indices.Get()[i++]; diff --git a/Source/Engine/Render2D/Render2D.cs b/Source/Engine/Render2D/Render2D.cs index c4d9e81b4..a73556f7b 100644 --- a/Source/Engine/Render2D/Render2D.cs +++ b/Source/Engine/Render2D/Render2D.cs @@ -152,6 +152,59 @@ namespace FlaxEngine DrawText(font, text, color, ref layout, customMaterial); } + /// + /// Draws a text. + /// + /// The fonts to use, ordered by priority. + /// The text to render. + /// The size and position of the area in which the text is drawn. + /// The text color. + /// The horizontal alignment of the text in a layout rectangle. + /// The vertical alignment of the text in a layout rectangle. + /// Describes how wrap text inside a layout rectangle. + /// The scale for distance one baseline from another. Default is 1. + /// The text drawing scale. Default is 1. + public static void DrawText(Font[] fonts, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + { + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + DrawText(fonts, text, color, ref layout); + } + + /// + /// Draws a text using a custom material shader. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). + /// + /// The fonts to use, ordered by priority. + /// Custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. + /// The text to render. + /// The size and position of the area in which the text is drawn. + /// The text color. + /// The horizontal alignment of the text in a layout rectangle. + /// The vertical alignment of the text in a layout rectangle. + /// Describes how wrap text inside a layout rectangle. + /// The scale for distance one baseline from another. Default is 1. + /// The text drawing scale. Default is 1. + public static void DrawText(Font[] fonts, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + { + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + DrawText(fonts, text, color, ref layout, customMaterial); + } + /// /// Calls drawing GUI to the texture. /// diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index 2b890ced9..0ec88edc0 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -33,7 +33,7 @@ API_CLASS(Static) class FLAXENGINE_API Render2D /// /// The rendering features and options flags. /// - API_ENUM(Attributes="Flags") enum class RenderingFeatures + API_ENUM(Attributes = "Flags") enum class RenderingFeatures { /// /// The none. @@ -215,6 +215,49 @@ public: /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. API_FUNCTION() static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + /// + /// Draws a text. + /// + /// The fonts to use, ordered by priority. + /// The text to render. + /// The input text range (substring range of the input text parameter). + /// The text color. + /// The text location. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. + API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + + /// + /// Draws a text with formatting. + /// + /// The fonts to use, ordered by priority. + /// The text to render. + /// The text color. + /// The text layout properties. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. + API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + + /// + /// Draws a text with formatting. + /// + /// The fonts to use, ordered by priority. + /// The text to render. + /// The input text range (substring range of the input text parameter). + /// The text color. + /// The text layout properties. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. + API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + + /// + /// Draws a text with formatting. + /// + /// The fonts to use, ordered by priority. + /// The text to render. + /// The input text range (substring range of the input text parameter). + /// The text color. + /// The text layout properties. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. + API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + /// /// Fills a rectangle area. /// diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index 188333ff1..8e71c9f31 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -295,12 +295,13 @@ namespace FlaxEngine // Use optionally bundled default font (matches Editor) var defaultFont = Content.LoadAsyncInternal("Editor/Fonts/Roboto-Regular"); + var cjkFont = Content.LoadAsyncInternal("NotoSansSC-Medium"); if (defaultFont) { style.FontTitle = defaultFont.CreateFont(18); style.FontLarge = defaultFont.CreateFont(14); - style.FontMedium = defaultFont.CreateFont(9); - style.FontSmall = defaultFont.CreateFont(9); + style.FontMedium = new Font[] { defaultFont.CreateFont(9), cjkFont.CreateFont(9) }; + style.FontSmall = new Font[] { defaultFont.CreateFont(9), cjkFont.CreateFont(9) }; } Style.Current = style; diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index 02337411b..1b965b8d1 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; namespace FlaxEngine.GUI { @@ -155,7 +156,7 @@ namespace FlaxEngine.GUI var style = Style.Current; if (style != null) { - _font = new FontReference(style.FontMedium); + _font = new FontReference(style.FontMedium.First()); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BorderColor = style.BorderNormal; diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index ecca2978f..21ca9cbea 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; namespace FlaxEngine.GUI { @@ -358,7 +359,7 @@ namespace FlaxEngine.GUI : base(0, 0, 120, 18.0f) { var style = Style.Current; - Font = new FontReference(style.FontMedium); + Font = new FontReference(style.FontMedium.First()); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BackgroundColorHighlighted = BackgroundColor; diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index 3c7c04fb2..b92e762e1 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -1,6 +1,8 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using FlaxEditor.Options; using System.ComponentModel; +using System.Linq; namespace FlaxEngine.GUI { @@ -190,7 +192,7 @@ namespace FlaxEngine.GUI { AutoFocus = false; var style = Style.Current; - Font = new FontReference(style.FontMedium); + Font = new FontReference(style.FontMedium.First()); TextColor = style.Foreground; TextColorHighlighted = style.Foreground; } @@ -201,7 +203,7 @@ namespace FlaxEngine.GUI { AutoFocus = false; var style = Style.Current; - Font = new FontReference(style.FontMedium); + Font = new FontReference(style.FontMedium.First()); TextColor = style.Foreground; TextColorHighlighted = style.Foreground; } @@ -233,7 +235,7 @@ namespace FlaxEngine.GUI } } - Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + Render2D.DrawText(new Font[] { _font.GetFont(), Style.Current.FontCJK }, Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); if (ClipText) Render2D.PopClip(); diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.cs b/Source/Engine/UI/GUI/Common/RichTextBox.cs index ab922d93d..e6a82c986 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System.Collections.Generic; +using System.Linq; namespace FlaxEngine.GUI { @@ -45,7 +46,7 @@ namespace FlaxEngine.GUI var style = Style.Current; _textStyle = new TextBlockStyle { - Font = new FontReference(style.FontMedium), + Font = new FontReference(style.FontMedium.First()), Color = style.Foreground, BackgroundSelectedBrush = new SolidColorBrush(style.BackgroundSelected), }; diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index ee4f744a6..967617649 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -1,5 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System.Linq; + namespace FlaxEngine.GUI { /// @@ -88,7 +90,7 @@ namespace FlaxEngine.GUI _layout.Bounds = new Rectangle(DefaultMargin, 1, Width - 2 * DefaultMargin, Height - 2); var style = Style.Current; - Font = new FontReference(style.FontMedium); + Font = new FontReference(style.FontMedium.First()); TextColor = style.Foreground; WatermarkTextColor = style.ForegroundDisabled; SelectionColor = style.BackgroundSelected; diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index 66e7413eb..33c2e1605 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; namespace FlaxEngine.GUI { @@ -237,7 +238,7 @@ namespace FlaxEngine.GUI var style = Style.Current; HeaderColor = style.BackgroundNormal; HeaderColorMouseOver = style.BackgroundHighlighted; - HeaderTextFont = new FontReference(style.FontMedium); + HeaderTextFont = new FontReference(style.FontMedium.First()); HeaderTextColor = style.Foreground; ArrowImageOpened = new SpriteBrush(style.ArrowDown); ArrowImageClosed = new SpriteBrush(style.ArrowRight); diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index 22b8f52af..d3375a670 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -1,5 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System.Linq; + namespace FlaxEngine.GUI { /// @@ -12,6 +14,14 @@ namespace FlaxEngine.GUI /// public static Style Current { get; set; } + public Font FontCJK + { + get => _fontCJK?.GetFont(); + set => _fontCJK = new FontReference(value); + } + + private FontReference _fontCJK; + [Serialize] private FontReference _fontTitle; @@ -41,31 +51,31 @@ namespace FlaxEngine.GUI } [Serialize] - private FontReference _fontMedium; + private FontReference[] _fontMedium; /// /// The font medium. /// [NoSerialize] [EditorOrder(30)] - public Font FontMedium + public Font[] FontMedium { - get => _fontMedium?.GetFont(); - set => _fontMedium = new FontReference(value); + get => _fontMedium?.Select((x)=>x.GetFont()).ToArray(); + set => _fontMedium = value.Select((x)=>new FontReference(x)).ToArray(); } [Serialize] - private FontReference _fontSmall; + private FontReference[] _fontSmall; /// /// The font small. /// [NoSerialize] [EditorOrder(40)] - public Font FontSmall + public Font[] FontSmall { - get => _fontSmall?.GetFont(); - set => _fontSmall = new FontReference(value); + get => _fontSmall?.Select((x) => x.GetFont()).ToArray(); + set => _fontSmall = value.Select((x) => new FontReference(x)).ToArray(); } /// diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index 734fb078f..e17b754c4 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; namespace FlaxEngine.GUI { @@ -255,14 +256,14 @@ namespace FlaxEngine.GUI // Calculate size of the tooltip var size = Float2.Zero; - if (style != null && style.FontMedium && !string.IsNullOrEmpty(_currentText)) + if (style != null && style.FontMedium.First() && !string.IsNullOrEmpty(_currentText)) { var layout = TextLayoutOptions.Default; layout.Bounds = new Rectangle(0, 0, MaxWidth, 10000000); layout.HorizontalAlignment = TextAlignment.Center; layout.VerticalAlignment = TextAlignment.Center; layout.TextWrapping = TextWrapping.WrapWords; - var items = style.FontMedium.ProcessText(_currentText, ref layout); + var items = style.FontMedium.First().ProcessText(_currentText, ref layout); for (int i = 0; i < items.Length; i++) { ref var item = ref items[i]; From a3bc394e4e0279c51b3acb133a6ad8ef5a2f27bf Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:04:29 +0800 Subject: [PATCH 02/16] Fix a bunch of rendering bugs --- .gitignore | 1 + Source/Editor/EditorAssets.cs | 2 +- Source/Engine/Render2D/Font.cpp | 133 ++++++++++---- Source/Engine/Render2D/Font.h | 39 +++- Source/Engine/Render2D/MultiFont.cpp | 1 + Source/Engine/Render2D/MultiFont.h | 77 ++++++++ Source/Engine/Render2D/Render2D.cpp | 266 +++++++++++++-------------- Source/Engine/UI/GUI/Common/Label.cs | 2 +- 8 files changed, 332 insertions(+), 189 deletions(-) create mode 100644 Source/Engine/Render2D/MultiFont.cpp create mode 100644 Source/Engine/Render2D/MultiFont.h diff --git a/.gitignore b/.gitignore index b7e11e554..b653b7f77 100644 --- a/.gitignore +++ b/.gitignore @@ -157,3 +157,4 @@ obj/ .idea/ *.code-workspace omnisharp.json +Content/Editor/Fonts/NotoSansSC-Regular.flax diff --git a/Source/Editor/EditorAssets.cs b/Source/Editor/EditorAssets.cs index 237908a0c..0fe5ee47e 100644 --- a/Source/Editor/EditorAssets.cs +++ b/Source/Editor/EditorAssets.cs @@ -54,7 +54,7 @@ namespace FlaxEditor /// public static string PrimaryFont = "Editor/Fonts/Roboto-Regular"; - public static string CJKFont = "Editor/Fonts/NotoSansSC-Medium"; + public static string CJKFont = "Editor/Fonts/NotoSansSC-Regular"; /// /// The Inconsolata Regular font. diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index 13b17197c..277ad8ddd 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -6,6 +6,7 @@ #include "Engine/Core/Log.h" #include "Engine/Threading/Threading.h" #include "IncludeFreeType.h" +#include "MultiFont.h" Font::Font(FontAsset* parentAsset, float size) : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)) @@ -118,6 +119,10 @@ void Font::ProcessText(const StringView& text, Array& outputLines tmpLine.FirstCharIndex = 0; tmpLine.LastCharIndex = -1; + if (textLength == 0) { + return; + } + int32 lastWrapCharIndex = INVALID_INDEX; float lastWrapCharX = 0; bool lastMoveLine = false; @@ -129,6 +134,11 @@ void Font::ProcessText(const StringView& text, Array& outputLines float xAdvance = 0; int32 nextCharIndex = currentIndex + 1; + // Submit line if text ends + if (nextCharIndex == textLength) { + moveLine = true; + } + // Cache current character const Char currentChar = text[currentIndex]; const bool isWhitespace = StringUtils::IsWhitespace(currentChar); @@ -146,7 +156,6 @@ void Font::ProcessText(const StringView& text, Array& outputLines { // Break line moveLine = true; - currentIndex++; tmpLine.LastCharIndex++; } else @@ -178,8 +187,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines if (lastWrapCharIndex != INVALID_INDEX) { // Skip moving twice for the same character - int32 lastLineLasCharIndex = outputLines.HasItems() ? outputLines.Last().LastCharIndex : -10000; - if (lastLineLasCharIndex == lastWrapCharIndex || lastLineLasCharIndex == lastWrapCharIndex - 1 || lastLineLasCharIndex == lastWrapCharIndex - 2) + int32 lastLineLastCharIndex = outputLines.HasItems() ? outputLines.Last().LastCharIndex : -10000; + if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2) { currentIndex = nextCharIndex; lastMoveLine = moveLine; @@ -226,8 +235,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines // Reset line tmpLine.Location.Y += baseLinesDistance; - tmpLine.FirstCharIndex = currentIndex; - tmpLine.LastCharIndex = currentIndex - 1; + tmpLine.FirstCharIndex = nextCharIndex; + tmpLine.LastCharIndex = nextCharIndex - 1; cursorX = 0; lastWrapCharIndex = INVALID_INDEX; lastWrapCharX = 0; @@ -238,7 +247,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines lastMoveLine = moveLine; } - if (textLength != 0 && (tmpLine.LastCharIndex >= tmpLine.FirstCharIndex || text[textLength - 1] == '\n')) + // Check if an additional line should be created + if (text[textLength - 1] == '\n') { // Add line tmpLine.Size.X = cursorX; @@ -283,84 +293,101 @@ void Font::ProcessText(const StringView& text, Array& outputLines } } -void Font::ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) +void Font::ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) { float cursorX = 0; int32 kerning; - FontLineCache tmpLine; + MultiFontLineCache tmpLine; + MultiFontSegmentCache tmpSegment; FontCharacterEntry entry; FontCharacterEntry previous; int32 textLength = text.Length(); float scale = layout.Scale / FontManager::FontScale; float boundsWidth = layout.Bounds.GetWidth(); float baseLinesDistanceScale = layout.BaseLinesGapScale * scale; + + tmpSegment.Location = Float2::Zero; + tmpSegment.Height = 0; + tmpSegment.FirstCharIndex = 0; + tmpSegment.LastCharIndex = -1; + tmpLine.Location = Float2::Zero; tmpLine.Size = Float2::Zero; - tmpLine.FirstCharIndex = 0; - tmpLine.LastCharIndex = -1; + tmpLine.Segments = Array(); + + if (textLength == 0) { + return; + } int32 lastWrapCharIndex = INVALID_INDEX; float lastWrapCharX = 0; bool lastMoveLine = false; - - int32 previousFontIndex = -1; + // The index of the font used by the current segment + int32 currentFontIndex = GetCharFontIndex(fonts, text[0], 0); // The maximum font height of the current line float maxHeight = 0; + float maxAscender = 0; + // Process each character to split text into single lines for (int32 currentIndex = 0; currentIndex < textLength;) { bool moveLine = false; + bool moveSegment = false; float xAdvance = 0; int32 nextCharIndex = currentIndex + 1; + // Submit line and segment if text ends + if (nextCharIndex == textLength) { + moveLine = moveSegment = true; + } + // Cache current character const Char currentChar = text[currentIndex]; const bool isWhitespace = StringUtils::IsWhitespace(currentChar); // Check if character can wrap words - const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar); + const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar) || (currentChar >= 0x3040 && currentChar <= 0x9FFF); if (isWrapChar && currentIndex != 0) { lastWrapCharIndex = currentIndex; lastWrapCharX = cursorX; } + int32 nextFontIndex = currentFontIndex; // Check if it's a newline character if (currentChar == '\n') { // Break line - moveLine = true; - currentIndex++; - tmpLine.LastCharIndex++; + moveLine = moveSegment = true; + tmpSegment.LastCharIndex++; } else { // Get character entry - int32 fontIndex = 0; - while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(currentChar)) - { - fontIndex++; + if (nextCharIndex < textLength) { + nextFontIndex = GetCharFontIndex(fonts, text[nextCharIndex], currentFontIndex); } - // If no font can match the char, then use the first font - if (fontIndex == fonts.Count()) { - fontIndex = 0; - } // Get character entry - fonts[fontIndex]->GetCharacter(currentChar, entry); - maxHeight = Math::Max(maxHeight, static_cast(fonts[fontIndex]->GetHeight())); + fonts[currentFontIndex]->GetCharacter(currentChar, entry); + maxHeight = Math::Max(maxHeight, static_cast(fonts[currentFontIndex]->GetHeight())); + maxAscender = Math::Max(maxAscender, static_cast(fonts[currentFontIndex]->GetAscender())); + + // Move segment if the font changes or text ends + if (nextFontIndex != currentFontIndex || nextCharIndex == textLength) { + moveSegment = true; + } // Get kerning, only when the font hasn't changed - if (!isWhitespace && previous.IsValid && previousFontIndex == fontIndex) + if (!isWhitespace && previous.IsValid && !moveSegment) { - kerning = fonts[fontIndex]->GetKerning(previous.Character, entry.Character); + kerning = fonts[currentFontIndex]->GetKerning(previous.Character, entry.Character); } else { kerning = 0; } previous = entry; - previousFontIndex = fontIndex; xAdvance = (kerning + entry.AdvanceX) * scale; // Check if character fits the line or skip wrapping @@ -368,15 +395,15 @@ void Font::ProcessText(const Array& fonts, const StringView& text, Array< { // Move character cursorX += xAdvance; - tmpLine.LastCharIndex++; + tmpSegment.LastCharIndex++; } else if (layout.TextWrapping == TextWrapping::WrapWords) { if (lastWrapCharIndex != INVALID_INDEX) { // Skip moving twice for the same character - int32 lastLineLasCharIndex = outputLines.HasItems() ? outputLines.Last().LastCharIndex : -10000; - if (lastLineLasCharIndex == lastWrapCharIndex || lastLineLasCharIndex == lastWrapCharIndex - 1 || lastLineLasCharIndex == lastWrapCharIndex - 2) + int32 lastLineLastCharIndex = outputLines.HasItems() && outputLines.Last().Segments.HasItems() ? outputLines.Last().Segments.Last().LastCharIndex : -10000; + if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2) { currentIndex = nextCharIndex; lastMoveLine = moveLine; @@ -386,16 +413,18 @@ void Font::ProcessText(const Array& fonts, const StringView& text, Array< // Move line const Char wrapChar = text[lastWrapCharIndex]; moveLine = true; + moveSegment = tmpSegment.FirstCharIndex < lastWrapCharIndex; + cursorX = lastWrapCharX; if (StringUtils::IsWhitespace(wrapChar)) { // Skip whitespaces - tmpLine.LastCharIndex = lastWrapCharIndex - 1; + tmpSegment.LastCharIndex = lastWrapCharIndex - 1; nextCharIndex = currentIndex = lastWrapCharIndex + 1; } else { - tmpLine.LastCharIndex = lastWrapCharIndex - 1; + tmpSegment.LastCharIndex = lastWrapCharIndex - 1; nextCharIndex = currentIndex = lastWrapCharIndex; } } @@ -404,6 +433,7 @@ void Font::ProcessText(const Array& fonts, const StringView& text, Array< { // Move line moveLine = true; + moveSegment = tmpSegment.FirstCharIndex < currentChar; nextCharIndex = currentIndex; // Skip moving twice for the same character @@ -412,38 +442,54 @@ void Font::ProcessText(const Array& fonts, const StringView& text, Array< } } + if (moveSegment) { + // Add segment + tmpSegment.Height = baseLinesDistanceScale * fonts[currentFontIndex]->GetHeight(); + tmpSegment.LastCharIndex = Math::Max(tmpSegment.LastCharIndex, tmpSegment.FirstCharIndex); + tmpSegment.FontIndex = currentFontIndex; + tmpLine.Segments.Add(tmpSegment); + + // Reset segment + tmpSegment.Location.X = cursorX; + tmpSegment.FirstCharIndex = nextCharIndex; + tmpSegment.LastCharIndex = nextCharIndex - 1; + + currentFontIndex = nextFontIndex; + } + // Check if move to another line if (moveLine) { // Add line tmpLine.Size.X = cursorX; tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - tmpLine.LastCharIndex = Math::Max(tmpLine.LastCharIndex, tmpLine.FirstCharIndex); + tmpLine.MaxAscender = maxAscender; outputLines.Add(tmpLine); // Reset line + tmpLine.Segments.Clear(); tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; - tmpLine.FirstCharIndex = currentIndex; - tmpLine.LastCharIndex = currentIndex - 1; cursorX = 0; + tmpSegment.Location.X = cursorX; lastWrapCharIndex = INVALID_INDEX; lastWrapCharX = 0; previous.IsValid = false; // Reset max font height maxHeight = 0; + maxAscender = 0; } currentIndex = nextCharIndex; lastMoveLine = moveLine; } - if (textLength != 0 && (tmpLine.LastCharIndex >= tmpLine.FirstCharIndex || text[textLength - 1] == '\n')) + // Check if an additional line should be created + if (text[textLength - 1] == '\n') { // Add line tmpLine.Size.X = cursorX; tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - tmpLine.LastCharIndex = textLength - 1; outputLines.Add(tmpLine); tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; @@ -466,7 +512,7 @@ void Font::ProcessText(const Array& fonts, const StringView& text, Array< } for (int32 i = 0; i < outputLines.Count(); i++) { - FontLineCache& line = outputLines[i]; + MultiFontLineCache& line = outputLines[i]; Float2 rootPos = line.Location + offset; // Fix upper left line corner to match desire text alignment @@ -480,6 +526,13 @@ void Font::ProcessText(const Array& fonts, const StringView& text, Array< } line.Location = rootPos; + + // Align all segments to center in case they have different heights + for (int32 j = 0; j < line.Segments.Count(); j++) + { + MultiFontSegmentCache& segment = line.Segments[j]; + segment.Location.Y += (line.MaxAscender - fonts[segment.FontIndex]->GetAscender()) / 2; + } } } diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index d8ac1b708..0425f2bc5 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -8,9 +8,11 @@ #include "Engine/Content/AssetReference.h" #include "Engine/Scripting/ScriptingObject.h" #include "TextLayoutOptions.h" +#include "MultiFont.h" class FontAsset; struct FontTextureAtlasSlot; +struct MultiFontLineCache; // The default DPI that engine is using #define DefaultDPI 96 @@ -20,7 +22,7 @@ struct FontTextureAtlasSlot; /// API_STRUCT(NoDefault) struct TextRange { -DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange); + DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange); /// /// The start index (inclusive). @@ -90,7 +92,7 @@ struct TIsPODType /// API_STRUCT(NoDefault) struct FontLineCache { -DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache); + DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache); /// /// The root position of the line (upper left corner). @@ -108,7 +110,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache); API_FIELD() int32 FirstCharIndex; /// - /// The last character index (from the input text). + /// The last character index (from the input text), inclusive. /// API_FIELD() int32 LastCharIndex; }; @@ -154,7 +156,7 @@ struct TIsPODType /// API_STRUCT(NoDefault) struct FontCharacterEntry { -DECLARE_SCRIPTING_TYPE_MINIMAL(FontCharacterEntry); + DECLARE_SCRIPTING_TYPE_MINIMAL(FontCharacterEntry); /// /// The character represented by this entry. @@ -223,7 +225,7 @@ struct TIsPODType /// API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API Font : public ManagedScriptingObject { -DECLARE_SCRIPTING_TYPE_NO_SPAWN(Font); + DECLARE_SCRIPTING_TYPE_NO_SPAWN(Font); friend FontAsset; private: @@ -346,7 +348,7 @@ public: /// The input text. /// The layout properties. /// The output lines list. - static void ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); + static void ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); /// /// Processes text to get cached lines for rendering. @@ -404,6 +406,7 @@ public: /// The minimum size for that text and fot to render properly. API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + /* /// /// Measures minimum size of the rectangle that will be needed to draw given text. /// @@ -412,6 +415,7 @@ public: /// The layout properties. /// The minimum size for that text and fot to render properly. API_FUNCTION() static Float2 MeasureText(const Array& fonts, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + */ /// /// Measures minimum size of the rectangle that will be needed to draw given text. @@ -500,6 +504,7 @@ public: /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + /* /// /// Calculates character position for given text and character index. /// @@ -509,6 +514,7 @@ public: /// The text layout properties. /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() static Float2 GetCharPosition(const Array& fonts, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + */ /// /// Calculates character position for given text and character index. @@ -553,6 +559,27 @@ public: /// True if the font contains the glyph of the char, otherwise false. API_FUNCTION() FORCE_INLINE bool ContainsChar(Char c); + /// + /// Gets the index of the font that should be used to render the char + /// + /// The font list. + /// The char. + /// Number to return if char cannot be found. + /// + API_FUNCTION() FORCE_INLINE static int32 GetCharFontIndex(const Array& fonts, Char c, int32 missing = -1) { + int32 fontIndex = 0; + while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(c)) + { + fontIndex++; + } + + if (fontIndex == fonts.Count()) { + return missing; + } + + return fontIndex; + } + /// /// Flushes the size of the face with the Free Type library backend. /// diff --git a/Source/Engine/Render2D/MultiFont.cpp b/Source/Engine/Render2D/MultiFont.cpp new file mode 100644 index 000000000..9844c12e0 --- /dev/null +++ b/Source/Engine/Render2D/MultiFont.cpp @@ -0,0 +1 @@ +#include "MultiFont.h" diff --git a/Source/Engine/Render2D/MultiFont.h b/Source/Engine/Render2D/MultiFont.h new file mode 100644 index 000000000..b1da832d5 --- /dev/null +++ b/Source/Engine/Render2D/MultiFont.h @@ -0,0 +1,77 @@ +#pragma once + +#include "Engine/Core/Collections/Array.h" +#include "Engine/Core/Collections/Dictionary.h" +#include "Font.h" + +/// +/// The font segment info generated during text processing. +/// +API_STRUCT(NoDefault) struct MultiFontSegmentCache +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(MultiFontSegmentCache); + + /// + /// The root position of the segment (upper left corner), relative to line. + /// + API_FIELD() Float2 Location; + + /// + /// The height of the current segment + /// + API_FIELD() float Height; + + /// + /// The first character index (from the input text). + /// + API_FIELD() int32 FirstCharIndex; + + /// + /// The last character index (from the input text), inclusive. + /// + API_FIELD() int32 LastCharIndex; + + /// + /// The index of the font to render with + /// + API_FIELD() int32 FontIndex; +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; + +/// +/// Line of font segments info generated during text processing. +/// +API_STRUCT(NoDefault) struct MultiFontLineCache +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(MultiFontLineCache); + + /// + /// The root position of the line (upper left corner). + /// + API_FIELD() Float2 Location; + + /// + /// The line bounds (width and height). + /// + API_FIELD() Float2 Size; + + /// + /// The maximum ascendent of the line. + /// + API_FIELD() float MaxAscender; + + /// + /// The index of the font to render with + /// + API_FIELD() Array Segments; +}; + +API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API MultiFont : public ManagedScriptingObject +{ + DECLARE_SCRIPTING_TYPE_NO_SPAWN(MultiFont); +}; diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index eedfbeffe..8a39d29be 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -3,6 +3,7 @@ #include "Render2D.h" #include "Font.h" #include "FontManager.h" +#include "MultiFont.h" #include "FontTextureAtlas.h" #include "RotatedRectangle.h" #include "SpriteAtlas.h" @@ -194,6 +195,7 @@ namespace // Drawing Array DrawCalls; Array Lines; + Array MultiFontLines; Array Lines2; bool IsScissorsRectEmpty; bool IsScissorsRectEnabled; @@ -1384,6 +1386,9 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const int32 kerning; float scale = 1.0f / FontManager::FontScale; + // Process text to get lines + Array maxAscenders; + // Render all characters FontCharacterEntry entry; Render2DDrawCall drawCall; @@ -1398,48 +1403,70 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const drawCall.AsChar.Mat = nullptr; } + int32 lineIndex = 0; + maxAscenders.Add(0); + for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) + { + if (text[currentIndex] != '\n') { + int32 fontIndex = Font::GetCharFontIndex(fonts, text[currentIndex], 0); + maxAscenders[lineIndex] = Math::Max(maxAscenders[lineIndex], static_cast(fonts[fontIndex]->GetAscender())); + } + else { + lineIndex++; + maxAscenders.Add(0); + } + } + + lineIndex = 0; // The following code cut the text into segments, according to the font used to render Float2 pointer = location; // The starting index of the current segment int32 startIndex = 0; // The index of the font used by the current segment - int32 segmentFontIndex = 0; + int32 currentFontIndex = Font::GetCharFontIndex(fonts, text[0], 0); // The maximum font height of the current line float maxHeight = 0; - for (int32 currentIndex = 0; currentIndex <= text.Length(); currentIndex++) + for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) { // Cache current character - const Char currentChar = currentIndex < text.Length() ? text[currentIndex] : 0; + const Char currentChar = text[currentIndex]; + int32 nextCharIndex = currentIndex + 1; + bool moveSegment = false; + bool moveLine = false; + int32 nextFontIndex = currentFontIndex; + + // Submit segment if text ends + if (nextCharIndex == text.Length()) { + moveSegment = true; + } // Check if it isn't a newline character if (currentChar != '\n') { - int32 fontIndex = 0; - if (currentIndex < text.Length()) { - while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(currentChar)) - { - fontIndex++; - } - - // If no font can match the char, then use the segment font - if (fontIndex == fonts.Count()) { - fontIndex = segmentFontIndex; - } - - // Do nothing if the char still belongs to the current segment - if (fontIndex == segmentFontIndex) { - continue; - } + // Get character entry + if (nextCharIndex < text.Length()) { + nextFontIndex = Font::GetCharFontIndex(fonts, text[nextCharIndex], currentFontIndex); } + if (nextFontIndex != currentFontIndex) { + moveSegment = true; + } + } + else + { + // Move + moveLine = moveSegment = true; + } + + if (moveSegment) { // Render the pending segment before beginning the new segment - renderText:auto fontHeight = fonts[segmentFontIndex]->GetHeight(); + auto fontHeight = fonts[currentFontIndex]->GetHeight(); maxHeight = Math::Max(maxHeight, static_cast(fontHeight)); - auto fontDescender = fonts[segmentFontIndex]->GetDescender(); - for (int32 renderIndex = startIndex; renderIndex < currentIndex; renderIndex++) + auto fontDescender = fonts[currentFontIndex]->GetDescender(); + for (int32 renderIndex = startIndex; renderIndex <= currentIndex; renderIndex++) { // Get character entry - fonts[segmentFontIndex]->GetCharacter(text[renderIndex], entry); + fonts[currentFontIndex]->GetCharacter(text[renderIndex], entry); // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) @@ -1466,7 +1493,7 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const // Get kerning if (!isWhitespace && previous.IsValid) { - kerning = fonts[segmentFontIndex]->GetKerning(previous.Character, entry.Character); + kerning = fonts[currentFontIndex]->GetKerning(previous.Character, entry.Character); } else { @@ -1482,7 +1509,7 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const const float x = pointer.X + entry.OffsetX * scale; const float y = pointer.Y + (fontHeight + fontDescender - entry.OffsetY) * scale; - Rectangle charRect(x, y, entry.UVSize.X * scale, entry.UVSize.Y * scale); + Rectangle charRect(x, y + (maxAscenders[lineIndex] - fonts[currentFontIndex]->GetAscender()) / 2, entry.UVSize.X * scale, entry.UVSize.Y * scale); Float2 upperLeftUV = entry.UV * invAtlasSize; Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; @@ -1498,22 +1525,17 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const pointer.X += entry.AdvanceX * scale; } - // Start new segment - startIndex = currentIndex; - segmentFontIndex = fontIndex; - - if (currentIndex == text.Length() - 1) { - currentIndex++; - goto renderText; + if (moveLine) { + pointer.X = location.X; + pointer.Y += maxHeight * scale; + // Clear max height + maxHeight = 0; + lineIndex++; } - } - else - { - // Move - pointer.X = location.X; - pointer.Y += maxHeight * scale; - // Clear max height - maxHeight = 0; + + // Start new segment + startIndex = nextCharIndex; + currentFontIndex = nextFontIndex; } } } @@ -1540,8 +1562,8 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const float scale = layout.Scale / FontManager::FontScale; // Process text to get lines - Lines.Clear(); - Font::ProcessText(fonts, text, Lines, layout); + MultiFontLines.Clear(); + Font::ProcessText(fonts, text, MultiFontLines, layout); // Render all lines FontCharacterEntry entry; @@ -1557,119 +1579,81 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const drawCall.AsChar.Mat = nullptr; } - for (int32 lineIndex = 0; lineIndex < Lines.Count(); lineIndex++) + for (int32 lineIndex = 0; lineIndex < MultiFontLines.Count(); lineIndex++) { - const FontLineCache& line = Lines[lineIndex]; - - // The following code cut the text into segments, according to the font used to render - Float2 pointer = line.Location; - // The starting index of the current segment - int32 startIndex = line.FirstCharIndex; - // The index of the font used by the current segment - int32 segmentFontIndex = 0; - // The maximum font height of the current line - float maxHeight = 0; - - // Partition and render all characters from the line - for (int32 charIndex = line.FirstCharIndex; charIndex <= line.LastCharIndex + 1; charIndex++) + const MultiFontLineCache& line = MultiFontLines[lineIndex]; + for (int32 segmentIndex = 0; segmentIndex < line.Segments.Count(); segmentIndex++) { - const Char c = charIndex <= line.LastCharIndex ? text[charIndex] : 0; + const MultiFontSegmentCache& segment = MultiFontLines[lineIndex].Segments[segmentIndex]; + auto fontHeight = fonts[segment.FontIndex]->GetHeight(); + auto fontDescender = fonts[segment.FontIndex]->GetDescender(); + Float2 pointer = line.Location + segment.Location; - if (c != '\n') + for (int32 charIndex = segment.FirstCharIndex; charIndex <= segment.LastCharIndex; charIndex++) { - int32 fontIndex = 0; - if (charIndex <= line.LastCharIndex) { - while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(c)) - { - fontIndex++; - } - - // If no font can match the char, then use the segment font - if (fontIndex == fonts.Count()) { - fontIndex = segmentFontIndex; - } - - - // Do nothing if the char still belongs to the current segment - if (fontIndex == segmentFontIndex) { - continue; - } - } - - - // Render the pending segment before beginning the new segment - auto fontHeight = fonts[segmentFontIndex]->GetHeight(); - maxHeight = Math::Max(maxHeight, static_cast(fontHeight)); - auto fontDescender = fonts[segmentFontIndex]->GetDescender(); - - const Char* pred = L"Type"; - if (text.Substring(0, Math::Min(4, text.Length())) == pred) { - // __debugbreak(); - } - for (int32 renderIndex = startIndex; renderIndex < charIndex; renderIndex++) + Char c = text[charIndex]; + if (c == '\n') { - // Get character entry - fonts[segmentFontIndex]->GetCharacter(text[renderIndex], entry); + continue; + } - // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) - if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) - { - // Get texture atlas that contains current character - fontAtlasIndex = entry.TextureIndex; - fontAtlas = FontManager::GetAtlas(fontAtlasIndex); - if (fontAtlas) - { - fontAtlas->EnsureTextureCreated(); - invAtlasSize = 1.0f / fontAtlas->GetSize(); - drawCall.AsChar.Tex = fontAtlas->GetTexture(); - } - else - { - invAtlasSize = 1.0f; - drawCall.AsChar.Tex = nullptr; - } - } + // Get character entry + fonts[segment.FontIndex]->GetCharacter(c, entry); - // Get kerning - const bool isWhitespace = StringUtils::IsWhitespace(text[renderIndex]); - if (!isWhitespace && previous.IsValid) + // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) + if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) + { + // Get texture atlas that contains current character + fontAtlasIndex = entry.TextureIndex; + fontAtlas = FontManager::GetAtlas(fontAtlasIndex); + if (fontAtlas) { - kerning = fonts[segmentFontIndex]->GetKerning(previous.Character, entry.Character); + fontAtlas->EnsureTextureCreated(); + invAtlasSize = 1.0f / fontAtlas->GetSize(); + drawCall.AsChar.Tex = fontAtlas->GetTexture(); } else { - kerning = 0; + invAtlasSize = 1.0f; + drawCall.AsChar.Tex = nullptr; } - pointer.X += (float)kerning * scale; - previous = entry; - - // Omit whitespace characters - if (!isWhitespace) - { - // Calculate character size and atlas coordinates - const float x = pointer.X + entry.OffsetX * scale; - const float y = pointer.Y - entry.OffsetY * scale + Math::Ceil((fontHeight + fontDescender) * scale); - - Rectangle charRect(x, y, entry.UVSize.X * scale, entry.UVSize.Y * scale); - charRect.Offset(layout.Bounds.Location); - - Float2 upperLeftUV = entry.UV * invAtlasSize; - Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; - - // Add draw call - drawCall.StartIB = IBIndex; - drawCall.CountIB = 6; - DrawCalls.Add(drawCall); - WriteRect(charRect, color, upperLeftUV, rightBottomUV); - } - - // Move - pointer.X += entry.AdvanceX * scale; } - // Start new segment - startIndex = charIndex; - segmentFontIndex = fontIndex; + // Get kerning + const bool isWhitespace = StringUtils::IsWhitespace(c); + if (!isWhitespace && previous.IsValid) + { + kerning = fonts[segment.FontIndex]->GetKerning(previous.Character, entry.Character); + } + else + { + kerning = 0; + } + pointer.X += (float)kerning * scale; + previous = entry; + + // Omit whitespace characters + if (!isWhitespace) + { + // Calculate character size and atlas coordinates + const float x = pointer.X + entry.OffsetX * scale; + const float y = pointer.Y - entry.OffsetY * scale + Math::Ceil((fontHeight + fontDescender) * scale); + + Rectangle charRect(x, y, entry.UVSize.X * scale, entry.UVSize.Y * scale); + charRect.Offset(layout.Bounds.Location); + + Float2 upperLeftUV = entry.UV * invAtlasSize; + Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; + + // Add draw call + drawCall.StartIB = IBIndex; + drawCall.CountIB = 6; + DrawCalls.Add(drawCall); + WriteRect(charRect, color, upperLeftUV, rightBottomUV); + } + + // Move + pointer.X += entry.AdvanceX * scale; } } } diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index b92e762e1..5fa8ad21d 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -235,7 +235,7 @@ namespace FlaxEngine.GUI } } - Render2D.DrawText(new Font[] { _font.GetFont(), Style.Current.FontCJK }, Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + Render2D.DrawText([_font.GetFont(), Style.Current.FontCJK], Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); if (ClipText) Render2D.PopClip(); From 41bbce56f6935d9066579a6b1d1e5d1b03c7fcc6 Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Thu, 30 Nov 2023 13:41:45 +0800 Subject: [PATCH 03/16] Add multifont rendering to editor --- .../Content/Create/CreateFilesDialog.cs | 2 +- .../Content/Import/ImportFilesDialog.cs | 2 +- Source/Editor/Content/Tree/ContentTreeNode.cs | 4 +- .../CustomEditors/Dedicated/RagdollEditor.cs | 2 +- .../CustomEditors/Dedicated/ScriptsEditor.cs | 2 +- .../Dedicated/UIControlEditor.cs | 6 +- .../Editors/ActorTransformEditor.cs | 2 +- .../Editor/CustomEditors/Editors/TagEditor.cs | 2 +- .../CustomEditors/LayoutElementsContainer.cs | 4 +- Source/Editor/EditorAssets.cs | 2 +- Source/Editor/GUI/ColumnDefinition.cs | 2 +- Source/Editor/GUI/ComboBox.cs | 6 +- .../GUI/ContextMenu/ContextMenuButton.cs | 6 +- Source/Editor/GUI/CurveEditor.cs | 4 +- Source/Editor/GUI/Docking/DockWindow.cs | 2 +- Source/Editor/GUI/ItemsListContextMenu.cs | 4 +- Source/Editor/GUI/MainMenu.cs | 8 +- Source/Editor/GUI/MainMenuButton.cs | 4 +- Source/Editor/GUI/NavigationButton.cs | 4 +- Source/Editor/GUI/Row.cs | 4 +- Source/Editor/GUI/Table.cs | 2 +- .../Editor/GUI/Timeline/GUI/PositionHandle.cs | 21 +- .../Editor/GUI/Timeline/Tracks/MemberTrack.cs | 2 +- Source/Editor/GUI/ToolStripButton.cs | 4 +- Source/Editor/GUI/Tree/TreeNode.cs | 8 +- Source/Editor/Options/InterfaceOptions.cs | 63 ++- Source/Editor/Options/OptionsModule.cs | 17 +- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 4 +- .../Archetypes/Animation.TransitionEditor.cs | 2 +- .../Editor/Surface/Archetypes/BehaviorTree.cs | 2 +- Source/Editor/Surface/Archetypes/Function.cs | 2 +- Source/Editor/Surface/AttributesEditor.cs | 2 +- .../Editor/Surface/ContextMenu/VisjectCM.cs | 2 +- .../Surface/ContextMenu/VisjectCMItem.cs | 14 +- Source/Editor/Surface/Elements/InputBox.cs | 4 +- Source/Editor/Surface/SurfaceNode.cs | 4 +- Source/Editor/Tools/Foliage/FoliageTab.cs | 2 +- Source/Editor/Tools/Terrain/CarveTab.cs | 2 +- .../Tools/Terrain/CreateTerrainDialog.cs | 2 +- Source/Editor/Viewport/EditorViewport.cs | 6 +- .../Viewport/Widgets/ViewportWidgetButton.cs | 4 +- Source/Editor/Windows/AboutDialog.cs | 4 +- Source/Editor/Windows/Assets/FontWindow.cs | 2 +- Source/Editor/Windows/ContentWindow.Search.cs | 2 +- Source/Editor/Windows/OutputLogWindow.cs | 74 +-- Source/Editor/Windows/PluginsWindow.cs | 9 +- Source/Editor/Windows/Profiler/Timeline.cs | 4 +- Source/Editor/Windows/ToolboxWindow.cs | 4 +- Source/Engine/Render2D/Font.cpp | 249 +--------- Source/Engine/Render2D/Font.h | 56 +-- Source/Engine/Render2D/MultiFont.cpp | 430 ++++++++++++++++++ Source/Engine/Render2D/MultiFont.h | 296 +++++++++++- Source/Engine/Render2D/MultiFontReference.cs | 74 +++ Source/Engine/Render2D/Render2D.cpp | 72 +-- Source/Engine/Render2D/Render2D.cs | 7 +- Source/Engine/Render2D/Render2D.h | 9 +- Source/Engine/Scripting/Scripting.cs | 15 +- Source/Engine/UI/GUI/Common/Button.cs | 8 +- Source/Engine/UI/GUI/Common/Dropdown.cs | 6 +- Source/Engine/UI/GUI/Common/Label.cs | 12 +- .../UI/GUI/Common/RichTextBox.Parsing.cs | 34 +- .../Engine/UI/GUI/Common/RichTextBox.Tags.cs | 72 +-- Source/Engine/UI/GUI/Common/RichTextBox.cs | 2 +- .../Engine/UI/GUI/Common/RichTextBoxBase.cs | 28 +- Source/Engine/UI/GUI/Common/TextBox.cs | 16 +- Source/Engine/UI/GUI/Panels/DropPanel.cs | 6 +- Source/Engine/UI/GUI/Style.cs | 40 +- Source/Engine/UI/GUI/TextBlockStyle.cs | 2 +- Source/Engine/UI/GUI/Tooltip.cs | 5 +- 69 files changed, 1132 insertions(+), 647 deletions(-) create mode 100644 Source/Engine/Render2D/MultiFontReference.cs diff --git a/Source/Editor/Content/Create/CreateFilesDialog.cs b/Source/Editor/Content/Create/CreateFilesDialog.cs index d48e878bc..48e4920bb 100644 --- a/Source/Editor/Content/Create/CreateFilesDialog.cs +++ b/Source/Editor/Content/Create/CreateFilesDialog.cs @@ -39,7 +39,7 @@ namespace FlaxEditor.Content.Create AnchorPreset = AnchorPresets.HorizontalStretchTop, Offsets = new Margin(0, 0, 0, 40), Parent = this, - Font = new FontReference(Style.Current.FontTitle) + Font = new MultiFontReference(Style.Current.FontTitle) }; var infoLabel = new Label { diff --git a/Source/Editor/Content/Import/ImportFilesDialog.cs b/Source/Editor/Content/Import/ImportFilesDialog.cs index 967583cf6..5a142d0f6 100644 --- a/Source/Editor/Content/Import/ImportFilesDialog.cs +++ b/Source/Editor/Content/Import/ImportFilesDialog.cs @@ -60,7 +60,7 @@ namespace FlaxEditor.Content.Import AnchorPreset = AnchorPresets.HorizontalStretchTop, Offsets = new Margin(0, 0, 0, 40), Parent = this, - Font = new FontReference(Style.Current.FontTitle) + Font = new MultiFontReference(Style.Current.FontTitle) }; var infoLabel = new Label { diff --git a/Source/Editor/Content/Tree/ContentTreeNode.cs b/Source/Editor/Content/Tree/ContentTreeNode.cs index 37c4e8dbb..ee9b463f7 100644 --- a/Source/Editor/Content/Tree/ContentTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentTreeNode.cs @@ -151,8 +151,8 @@ namespace FlaxEditor.Content var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.First().GetCharPosition(text, ranges[i].StartIndex); - var end = font.First().GetCharPosition(text, ranges[i].EndIndex); + var start = font.GetCharPosition(text, ranges[i].StartIndex); + var end = font.GetCharPosition(text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs index c4b334b3a..b6d14d81e 100644 --- a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs @@ -81,7 +81,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new FontReference(FlaxEngine.GUI.Style.Current.FontLarge), + Font = new MultiFontReference(FlaxEngine.GUI.Style.Current.FontLarge), Text = "Ragdoll Options", Parent = this }; diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 1f69074ce..fd56422cb 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -43,7 +43,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Add script button var buttonText = "Add script"; - var textSize = Style.Current.FontMedium.First().MeasureText(buttonText); + var textSize = Style.Current.FontMedium.MeasureText(buttonText); float addScriptButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var buttonHeight = (textSize.Y < 18) ? 18 : textSize.Y + 4; _addScriptsButton = new Button diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index 5b5c2f90d..9627edfd8 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -239,7 +239,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Title var title = new Label(2, 2, DialogWidth - 4, TitleHeight) { - Font = new FontReference(style.FontLarge), + Font = new MultiFontReference(style.FontLarge), Text = "Anchor Presets", Parent = this }; @@ -247,7 +247,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Info var info = new Label(0, title.Bottom, DialogWidth, InfoHeight) { - Font = new FontReference(style.FontSmall.First()), + Font = new MultiFontReference(style.FontSmall), Text = "Shift: also set bounds\nControl: also set pivot", Parent = this }; @@ -423,7 +423,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Set control type button var space = layout.Space(20); var buttonText = "Set Type"; - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.First().MeasureText(buttonText); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); float setTypeButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var setTypeButton = new Button { diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index d7059f712..0cad200fd 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -101,7 +101,7 @@ namespace FlaxEditor.CustomEditors.Editors _linkButton.Clicked += ToggleLink; ToggleEnabled(); SetLinkStyle(); - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.First().MeasureText(LinkedLabel.Text.Value); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(LinkedLabel.Text.Value); _linkButton.LocalX += textSize.X + 10; LinkedLabel.SetupContextMenu += (label, menu, editor) => { diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs index a174d02e4..dbd5d124c 100644 --- a/Source/Editor/CustomEditors/Editors/TagEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs @@ -631,7 +631,7 @@ namespace FlaxEditor.CustomEditors.Editors TooltipText = "Edit...", Parent = _label, }; - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.First().MeasureText(buttonText); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); if (textSize.Y > button.Width) button.Width = textSize.Y + 2; diff --git a/Source/Editor/CustomEditors/LayoutElementsContainer.cs b/Source/Editor/CustomEditors/LayoutElementsContainer.cs index 936851b15..1584e88de 100644 --- a/Source/Editor/CustomEditors/LayoutElementsContainer.cs +++ b/Source/Editor/CustomEditors/LayoutElementsContainer.cs @@ -276,7 +276,7 @@ namespace FlaxEditor.CustomEditors public LabelElement Header(string text) { var element = Label(text); - element.Label.Font = new FontReference(Style.Current.FontLarge); + element.Label.Font = new MultiFontReference(Style.Current.FontLarge); return element; } @@ -284,7 +284,7 @@ namespace FlaxEditor.CustomEditors { var element = Header(header.Text); if (header.FontSize != -1) - element.Label.Font = new FontReference(element.Label.Font.Font, header.FontSize); + element.Label.Font = new MultiFontReference(element.Label.Font, header.FontSize); if (header.Color != 0) element.Label.TextColor = Color.FromRGBA(header.Color); return element; diff --git a/Source/Editor/EditorAssets.cs b/Source/Editor/EditorAssets.cs index 0fe5ee47e..c894abe6b 100644 --- a/Source/Editor/EditorAssets.cs +++ b/Source/Editor/EditorAssets.cs @@ -54,7 +54,7 @@ namespace FlaxEditor /// public static string PrimaryFont = "Editor/Fonts/Roboto-Regular"; - public static string CJKFont = "Editor/Fonts/NotoSansSC-Regular"; + public static string CjkFont = "Editor/Fonts/NotoSansSC-Regular"; /// /// The Inconsolata Regular font. diff --git a/Source/Editor/GUI/ColumnDefinition.cs b/Source/Editor/GUI/ColumnDefinition.cs index aff1817c3..c6e8f2889 100644 --- a/Source/Editor/GUI/ColumnDefinition.cs +++ b/Source/Editor/GUI/ColumnDefinition.cs @@ -35,7 +35,7 @@ namespace FlaxEditor.GUI /// /// The title font. /// - public Font TitleFont; + public MultiFont TitleFont; /// /// The column title text color. diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index 7dc407698..8e6cf39a0 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -191,7 +191,7 @@ namespace FlaxEditor.GUI /// Gets or sets the font used to draw text. /// [EditorDisplay("Style"), EditorOrder(2000)] - public FontReference Font { get; set; } + public MultiFontReference Font { get; set; } /// /// Gets or sets the color of the text. @@ -273,7 +273,7 @@ namespace FlaxEditor.GUI MaximumItemsInViewCount = 20; var style = Style.Current; - Font = new FontReference(style.FontMedium.First()); + Font = new MultiFontReference(style.FontMedium); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BackgroundColorHighlighted = BackgroundColor; @@ -554,7 +554,7 @@ namespace FlaxEditor.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + Render2D.DrawText(Font.GetMultiFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); } diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index 872700a9b..ba3326412 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -234,11 +234,11 @@ namespace FlaxEditor.GUI.ContextMenu { var style = Style.Current; float width = 20; - if (style.FontMedium.First()) + if (style.FontMedium) { - width += style.FontMedium.First().MeasureText(Text).X; + width += style.FontMedium.MeasureText(Text).X; if (!string.IsNullOrEmpty(ShortKeys)) - width += 40 + style.FontMedium.First().MeasureText(ShortKeys).X; + width += 40 + style.FontMedium.MeasureText(ShortKeys).X; } return Mathf.Max(width, base.MinimumWidth); diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index f71409b20..ff14cdb24 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -317,7 +317,7 @@ namespace FlaxEditor.GUI private Color _contentsColor; private Color _linesColor; private Color _labelsColor; - private Font _labelsFont; + private MultiFont _labelsFont; /// /// The keyframe UI points. @@ -437,7 +437,7 @@ namespace FlaxEditor.GUI _contentsColor = style.Background.RGBMultiplied(0.7f); _linesColor = style.ForegroundDisabled.RGBMultiplied(0.7f); _labelsColor = style.ForegroundDisabled; - _labelsFont = style.FontSmall.First(); + _labelsFont = style.FontSmall; _mainPanel = new Panel(ScrollBars.Both) { diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index 8601b9c53..374885f01 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -489,7 +489,7 @@ namespace FlaxEditor.GUI.Docking { var style = Style.Current; if (style?.FontMedium != null) - _titleSize = style.FontMedium.First().MeasureText(_title); + _titleSize = style.FontMedium.MeasureText(_title); } base.PerformLayoutBeforeChildren(); diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index ce69f3544..b823cc907 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -87,8 +87,8 @@ namespace FlaxEditor.GUI var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = font.First().GetCharPosition(Name, ranges[i].StartIndex); - var end = font.First().GetCharPosition(Name, ranges[i].EndIndex); + var start = font.GetCharPosition(Name, ranges[i].StartIndex); + var end = font.GetCharPosition(Name, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + 2, 0, end.X - start.X, Height)); } Visible = true; diff --git a/Source/Editor/GUI/MainMenu.cs b/Source/Editor/GUI/MainMenu.cs index b313fed9f..aadfbf0a7 100644 --- a/Source/Editor/GUI/MainMenu.cs +++ b/Source/Editor/GUI/MainMenu.cs @@ -76,7 +76,7 @@ namespace FlaxEditor.GUI var windowIcon = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.WindowIcon); FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.WindowIconsFont); - Font iconFont = windowIconsFont?.CreateFont(9); + MultiFont iconFont = new MultiFontReference([windowIconsFont], 9).GetMultiFont(); _window = mainWindow.RootWindow.Window; _window.HitTest += OnHitTest; @@ -108,7 +108,7 @@ namespace FlaxEditor.GUI _closeButton = new Button { Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(), - Font = new FontReference(iconFont), + Font = new MultiFontReference(iconFont), BackgroundColor = Color.Transparent, BorderColor = Color.Transparent, BorderColorHighlighted = Color.Transparent, @@ -124,7 +124,7 @@ namespace FlaxEditor.GUI _minimizeButton = new Button { Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(), - Font = new FontReference(iconFont), + Font = new MultiFontReference(iconFont), BackgroundColor = Color.Transparent, BorderColor = Color.Transparent, BorderColorHighlighted = Color.Transparent, @@ -139,7 +139,7 @@ namespace FlaxEditor.GUI _maximizeButton = new Button { Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(), - Font = new FontReference(iconFont), + Font = new MultiFontReference(iconFont), BackgroundColor = Color.Transparent, BorderColor = Color.Transparent, BorderColorHighlighted = Color.Transparent, diff --git a/Source/Editor/GUI/MainMenuButton.cs b/Source/Editor/GUI/MainMenuButton.cs index 76687f8b9..3bc57479c 100644 --- a/Source/Editor/GUI/MainMenuButton.cs +++ b/Source/Editor/GUI/MainMenuButton.cs @@ -102,8 +102,8 @@ namespace FlaxEditor.GUI var style = Style.Current; float width = 18; - if (style.FontMedium.First()) - width += style.FontMedium.First().MeasureText(Text).X; + if (style.FontMedium) + width += style.FontMedium.MeasureText(Text).X; Width = width; } diff --git a/Source/Editor/GUI/NavigationButton.cs b/Source/Editor/GUI/NavigationButton.cs index 77f8a7656..6fd17332c 100644 --- a/Source/Editor/GUI/NavigationButton.cs +++ b/Source/Editor/GUI/NavigationButton.cs @@ -66,9 +66,9 @@ namespace FlaxEditor.GUI { var style = Style.Current; - if (style.FontMedium.First()) + if (style.FontMedium) { - Width = style.FontMedium.First().MeasureText(Text).X + 2 * DefaultMargin; + Width = style.FontMedium.MeasureText(Text).X + 2 * DefaultMargin; } } } diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index 458138aea..7533dfb17 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -39,8 +39,8 @@ namespace FlaxEditor.GUI { Depth = -1; - if (Height < Style.Current.FontMedium.First().Height) - Height = Style.Current.FontMedium.First().Height + 4; + if (Height < Style.Current.FontMedium.MaxHeight) + Height = Style.Current.FontMedium.MaxHeight + 4; } /// diff --git a/Source/Editor/GUI/Table.cs b/Source/Editor/GUI/Table.cs index fed33a336..54656a6c2 100644 --- a/Source/Editor/GUI/Table.cs +++ b/Source/Editor/GUI/Table.cs @@ -130,7 +130,7 @@ namespace FlaxEditor.GUI Render2D.FillRectangle(rect, column.TitleBackgroundColor); var style = Style.Current; - var font = column.TitleFont ?? style.FontMedium.First(); + var font = column.TitleFont ?? style.FontMedium; Render2D.DrawText(font, column.Title, rect, column.TitleColor, TextAlignment.Center, TextAlignment.Center); if (columnIndex < _columns.Length - 1) diff --git a/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs b/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs index bedb61a5e..791fb7133 100644 --- a/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs +++ b/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs @@ -2,6 +2,7 @@ using System; using System.Globalization; +using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -36,16 +37,16 @@ namespace FlaxEditor.GUI.Timeline.GUI string labelText; switch (_timeline.TimeShowMode) { - case Timeline.TimeShowModes.Frames: - labelText = _timeline.CurrentFrame.ToString("###0", CultureInfo.InvariantCulture); - break; - case Timeline.TimeShowModes.Seconds: - labelText = _timeline.CurrentTime.ToString("###0.##'s'", CultureInfo.InvariantCulture); - break; - case Timeline.TimeShowModes.Time: - labelText = TimeSpan.FromSeconds(_timeline.CurrentTime).ToString("g"); - break; - default: throw new ArgumentOutOfRangeException(); + case Timeline.TimeShowModes.Frames: + labelText = _timeline.CurrentFrame.ToString("###0", CultureInfo.InvariantCulture); + break; + case Timeline.TimeShowModes.Seconds: + labelText = _timeline.CurrentTime.ToString("###0.##'s'", CultureInfo.InvariantCulture); + break; + case Timeline.TimeShowModes.Time: + labelText = TimeSpan.FromSeconds(_timeline.CurrentTime).ToString("g"); + break; + default: throw new ArgumentOutOfRangeException(); } var color = (_timeline.IsMovingPositionHandle ? style.ProgressNormal : style.Foreground).AlphaMultiplied(0.6f); Matrix3x3.RotationZ(Mathf.PiOverTwo, out var m1); diff --git a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs index 928129917..63787df2c 100644 --- a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs @@ -345,7 +345,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks if (_previewValue != null) { // Based on Track.Draw for track text placement - var left = _xOffset + 16 + Style.Current.FontSmall.First().MeasureText(Title ?? Name).X; + var left = _xOffset + 16 + Style.Current.FontSmall.MeasureText(Title ?? Name).X; if (Icon.IsValid) left += 18; if (IsExpanded) diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index b74c7c19f..d21fd5689 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -151,8 +151,8 @@ namespace FlaxEditor.GUI if (hasSprite) width += iconSize; - if (!string.IsNullOrEmpty(_text) && style.FontMedium.First()) - width += style.FontMedium.First().MeasureText(_text).X + (hasSprite ? DefaultMargin : 0); + if (!string.IsNullOrEmpty(_text) && style.FontMedium) + width += style.FontMedium.MeasureText(_text).X + (hasSprite ? DefaultMargin : 0); Width = width; } diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index f26929993..acb67ea8f 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -115,7 +115,7 @@ namespace FlaxEditor.GUI.Tree /// Gets or sets the font used to render text. /// [EditorDisplay("Style"), EditorOrder(2000)] - public FontReference TextFont { get; set; } + public MultiFontReference TextFont { get; set; } /// /// Gets or sets the color of the background when tree node is selected. @@ -318,7 +318,7 @@ namespace FlaxEditor.GUI.Tree BackgroundColorSelected = style.BackgroundSelected; BackgroundColorHighlighted = style.BackgroundHighlighted; BackgroundColorSelectedUnfocused = style.LightBackground; - TextFont = new FontReference(style.FontSmall.First()); + TextFont = new MultiFontReference(style.FontSmall); } /// @@ -573,7 +573,7 @@ namespace FlaxEditor.GUI.Tree { if (_textChanged) { - var font = TextFont.GetFont(); + var font = TextFont.GetMultiFont(); if (font) { _textWidth = font.MeasureText(_text).X; @@ -657,7 +657,7 @@ namespace FlaxEditor.GUI.Tree } // Draw text - Render2D.DrawText(TextFont.GetFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(TextFont.GetMultiFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); // Draw drag and drop effect if (IsDragOver && _tree.DraggedOverNode == this) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index c96a52fb1..e43751fd1 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -166,15 +166,13 @@ namespace FlaxEditor.Options /// Gets or sets the output log text font. /// [EditorDisplay("Output Log", "Text Font"), EditorOrder(320), Tooltip("The output log text font.")] - public FontReference OutputLogTextFont + public MultiFontReference OutputLogTextFont { get => _outputLogFont; set { - if (value == null) - _outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont), 10); - else if (!value.Font) - _outputLogFont.Font = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont); + if (value == null || !value.Verify()) + _outputLogFont = new MultiFontReference(ConsoleFonts, 10); else _outputLogFont = value; } @@ -236,32 +234,31 @@ namespace FlaxEditor.Options [EditorDisplay("Cook & Run"), EditorOrder(500)] public int NumberOfGameClientsToLaunch = 1; - private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); - private static FontAsset _cjkFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CJKFont); - private FontReference _titleFont = new FontReference(DefaultFont, 18); - private FontReference _largeFont = new FontReference(DefaultFont, 14); - private FontReference _mediumFont = new FontReference(DefaultFont, 9); - private FontReference _smallFont = new FontReference(DefaultFont, 9); - private FontReference _outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont), 10); + private static FontAsset[] DefaultFonts => + [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont), + FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CjkFont)]; + + private static FontAsset[] ConsoleFonts => [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont), + FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CjkFont)]; + + private MultiFontReference _titleFont = new MultiFontReference(DefaultFonts, 18); + private MultiFontReference _largeFont = new MultiFontReference(DefaultFonts, 14); + private MultiFontReference _mediumFont = new MultiFontReference(DefaultFonts, 9); + private MultiFontReference _smallFont = new MultiFontReference(DefaultFonts, 9); + private MultiFontReference _outputLogFont = new MultiFontReference(ConsoleFonts, 10); - public FontReference CJKFont - { - get => new FontReference(_cjkFont, 9); - } /// /// Gets or sets the title font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(600), Tooltip("The title font for editor UI.")] - public FontReference TitleFont + public MultiFontReference TitleFont { get => _titleFont; set { - if (value == null) - _titleFont = new FontReference(DefaultFont, 18); - else if (!value.Font) - _titleFont.Font = DefaultFont; + if (value == null || !value.Verify()) + _titleFont = new MultiFontReference(DefaultFonts, 18); else _titleFont = value; } @@ -271,15 +268,13 @@ namespace FlaxEditor.Options /// Gets or sets the large font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(610), Tooltip("The large font for editor UI.")] - public FontReference LargeFont + public MultiFontReference LargeFont { get => _largeFont; set { - if (value == null) - _largeFont = new FontReference(DefaultFont, 14); - else if (!value.Font) - _largeFont.Font = DefaultFont; + if (value == null || !value.Verify()) + _largeFont = new MultiFontReference(DefaultFonts, 14); else _largeFont = value; } @@ -289,15 +284,13 @@ namespace FlaxEditor.Options /// Gets or sets the medium font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(620), Tooltip("The medium font for editor UI.")] - public FontReference MediumFont + public MultiFontReference MediumFont { get => _mediumFont; set { - if (value == null) - _mediumFont = new FontReference(DefaultFont, 9); - else if (!value.Font) - _mediumFont.Font = DefaultFont; + if (value == null || !value.Verify()) + _mediumFont = new MultiFontReference(DefaultFonts, 9); else _mediumFont = value; } @@ -307,15 +300,13 @@ namespace FlaxEditor.Options /// Gets or sets the small font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(630), Tooltip("The small font for editor UI.")] - public FontReference SmallFont + public MultiFontReference SmallFont { get => _smallFont; set { - if (value == null) - _smallFont = new FontReference(DefaultFont, 9); - else if (!value.Font) - _smallFont.Font = DefaultFont; + if (value == null || !value.Verify()) + _smallFont = new MultiFontReference(DefaultFonts, 9); else _smallFont = value; } diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index 15059bd56..d138d7d0d 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -259,11 +259,10 @@ namespace FlaxEditor.Options }, // Fonts - FontTitle = options.Interface.TitleFont.GetFont(), - FontLarge = options.Interface.LargeFont.GetFont(), - FontMedium = new Font[] { options.Interface.MediumFont.GetFont(), options.Interface.CJKFont.GetFont() }, - FontSmall = new Font[] { options.Interface.SmallFont.GetFont(), options.Interface.CJKFont.GetFont() }, - FontCJK = options.Interface.CJKFont.GetFont(), + FontTitle = options.Interface.TitleFont.GetMultiFont(), + FontLarge = options.Interface.LargeFont.GetMultiFont(), + FontMedium = options.Interface.MediumFont.GetMultiFont(), + FontSmall = options.Interface.SmallFont.GetMultiFont(), // Icons ArrowDown = Editor.Icons.ArrowDown12, @@ -313,10 +312,10 @@ namespace FlaxEditor.Options ProgressNormal = new Color(0.03f, 0.65f, 0.12f, 1f), // Fonts - FontTitle = options.Interface.TitleFont.GetFont(), - FontLarge = options.Interface.LargeFont.GetFont(), - FontMedium = new Font[] { options.Interface.MediumFont.GetFont(), options.Interface.CJKFont.GetFont() }, - FontSmall = new Font[] { options.Interface.SmallFont.GetFont(), options.Interface.CJKFont.GetFont() }, + FontTitle = options.Interface.TitleFont.GetMultiFont(), + FontLarge = options.Interface.LargeFont.GetMultiFont(), + FontMedium = options.Interface.MediumFont.GetMultiFont(), + FontSmall = options.Interface.SmallFont.GetMultiFont(), // Icons ArrowDown = Editor.Icons.ArrowDown12, diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index 9a6fe4fd2..f64e46385 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -142,8 +142,8 @@ namespace FlaxEditor.SceneGraph.GUI var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.First().GetCharPosition(text, ranges[i].StartIndex); - var end = font.First().GetCharPosition(text, ranges[i].EndIndex); + var start = font.GetCharPosition(text, ranges[i].StartIndex); + var end = font.GetCharPosition(text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs b/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs index 84c2144b2..e1fdb0a42 100644 --- a/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs +++ b/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs @@ -39,7 +39,7 @@ namespace FlaxEditor.Surface.Archetypes // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new FontReference(Style.Current.FontLarge), + Font = new MultiFontReference(Style.Current.FontLarge), Text = transition.SurfaceName, Parent = this }; diff --git a/Source/Editor/Surface/Archetypes/BehaviorTree.cs b/Source/Editor/Surface/Archetypes/BehaviorTree.cs index 38dd28e62..cca6856ae 100644 --- a/Source/Editor/Surface/Archetypes/BehaviorTree.cs +++ b/Source/Editor/Surface/Archetypes/BehaviorTree.cs @@ -101,7 +101,7 @@ namespace FlaxEditor.Surface.Archetypes _debugRelevant = Behavior.GetNodeDebugRelevancy(instance, behavior); _debugInfo = Behavior.GetNodeDebugInfo(instance, behavior); if (!string.IsNullOrEmpty(_debugInfo)) - _debugInfoSize = Style.Current.FontSmall.First().MeasureText(_debugInfo); + _debugInfoSize = Style.Current.FontSmall.MeasureText(_debugInfo); } } diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index 53950dad2..bc982a510 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -1407,7 +1407,7 @@ namespace FlaxEditor.Surface.Archetypes // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new FontReference(Style.Current.FontLarge), + Font = new MultiFontReference(Style.Current.FontLarge), Text = "Edit function signature", Parent = this }; diff --git a/Source/Editor/Surface/AttributesEditor.cs b/Source/Editor/Surface/AttributesEditor.cs index 81b11bb68..c9e32e23a 100644 --- a/Source/Editor/Surface/AttributesEditor.cs +++ b/Source/Editor/Surface/AttributesEditor.cs @@ -83,7 +83,7 @@ namespace FlaxEditor.Surface // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new FontReference(Style.Current.FontLarge), + Font = new MultiFontReference(Style.Current.FontLarge), Text = "Edit attributes", Parent = this }; diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 930741807..0624d44b3 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -141,7 +141,7 @@ namespace FlaxEditor.Surface.ContextMenu }; // Title bar - var titleFontReference = new FontReference(Style.Current.FontLarge.Asset, 10); + var titleFontReference = new MultiFontReference(Style.Current.FontLarge); var titleLabel = new Label { Width = Width * 0.5f - 8f, diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 812bec5d2..207875a92 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -200,8 +200,8 @@ namespace FlaxEditor.Surface.ContextMenu var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = font.First().GetCharPosition(_archetype.Title, ranges[i].StartIndex); - var end = font.First().GetCharPosition(_archetype.Title, ranges[i].EndIndex); + var start = font.GetCharPosition(_archetype.Title, ranges[i].StartIndex); + var end = font.GetCharPosition(_archetype.Title, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); if (ranges[i].StartIndex <= 0) @@ -222,8 +222,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = font.First().GetCharPosition(_archetype.Title, 0); - var end = font.First().GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); + var start = font.GetCharPosition(_archetype.Title, 0); + var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); _isFullMatch = true; Visible = true; @@ -237,8 +237,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = font.First().GetCharPosition(_archetype.Title, 0); - var end = font.First().GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); + var start = font.GetCharPosition(_archetype.Title, 0); + var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); Visible = true; @@ -286,7 +286,7 @@ namespace FlaxEditor.Surface.ContextMenu Render2D.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); if (_archetype.SubTitle != null) { - var titleLength = style.FontSmall.First().MeasureText(_archetype.Title).X; + var titleLength = style.FontSmall.MeasureText(_archetype.Title).X; var subTitleRect = new Rectangle(textRect.X + titleLength, textRect.Y, textRect.Width - titleLength, textRect.Height); Render2D.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } diff --git a/Source/Editor/Surface/Elements/InputBox.cs b/Source/Editor/Surface/Elements/InputBox.cs index 611160611..2047bcd1a 100644 --- a/Source/Editor/Surface/Elements/InputBox.cs +++ b/Source/Editor/Surface/Elements/InputBox.cs @@ -1428,7 +1428,7 @@ namespace FlaxEditor.Surface.Elements if (_defaultValueEditor != null) { - _defaultValueEditor.Location = new Float2(X + Width + 8 + Style.Current.FontSmall.First().MeasureText(Text).X, Y); + _defaultValueEditor.Location = new Float2(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y); } } @@ -1635,7 +1635,7 @@ namespace FlaxEditor.Surface.Elements { if (DefaultValueEditors[i].CanUse(this, ref _currentType)) { - var bounds = new Rectangle(X + Width + 8 + Style.Current.FontSmall.First().MeasureText(Text).X, Y, 90, Height); + var bounds = new Rectangle(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y, 90, Height); _editor = DefaultValueEditors[i]; try { diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 82a9ab2bd..b6436e542 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -200,7 +200,7 @@ namespace FlaxEditor.Surface continue; if (child is InputBox inputBox) { - var boxWidth = boxLabelFont.First().MeasureText(inputBox.Text).X + 20; + var boxWidth = boxLabelFont.MeasureText(inputBox.Text).X + 20; if (inputBox.DefaultValueEditor != null) boxWidth += inputBox.DefaultValueEditor.Width + 4; leftWidth = Mathf.Max(leftWidth, boxWidth); @@ -208,7 +208,7 @@ namespace FlaxEditor.Surface } else if (child is OutputBox outputBox) { - rightWidth = Mathf.Max(rightWidth, boxLabelFont.First().MeasureText(outputBox.Text).X + 20); + rightWidth = Mathf.Max(rightWidth, boxLabelFont.MeasureText(outputBox.Text).X + 20); rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } else if (child is Control control) diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index e88972b5c..1badb8c0c 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -148,7 +148,7 @@ namespace FlaxEditor.Tools.Foliage Parent = _noFoliagePanel, Enabled = false }; - var textSize = Style.Current.FontMedium.First().MeasureText(buttonText); + var textSize = Style.Current.FontMedium.MeasureText(buttonText); if (_createNewFoliage.Width < textSize.X) { _createNewFoliage.LocalX -= (textSize.X - _createNewFoliage.Width) / 2; diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index 0a43cd2d2..4ff85ca23 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -106,7 +106,7 @@ namespace FlaxEditor.Tools.Terrain Parent = _noTerrainPanel, Enabled = false }; - var textSize = Style.Current.FontMedium.First().MeasureText(buttonText); + var textSize = Style.Current.FontMedium.MeasureText(buttonText); if (_createTerrainButton.Width < textSize.X) { _createTerrainButton.LocalX -= (textSize.X - _createTerrainButton.Width) / 2; diff --git a/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs b/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs index 252891d44..cba52283d 100644 --- a/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs +++ b/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs @@ -96,7 +96,7 @@ namespace FlaxEditor.Tools.Terrain AnchorPreset = AnchorPresets.HorizontalStretchTop, Offsets = new Margin(0, 0, 0, 40), Parent = this, - Font = new FontReference(Style.Current.FontTitle) + Font = new MultiFontReference(Style.Current.FontTitle) }; var infoLabel = new Label { diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index f4ebfb51b..c49392d01 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -548,9 +548,9 @@ namespace FlaxEditor.Viewport #region Camera settings widget var largestText = "Relative Panning"; - var textSize = Style.Current.FontMedium.First().MeasureText(largestText); + var textSize = Style.Current.FontMedium.MeasureText(largestText); var xLocationForExtras = textSize.X + 5; - var cameraSpeedTextWidth = Style.Current.FontMedium.First().MeasureText("0.00").X; + var cameraSpeedTextWidth = Style.Current.FontMedium.MeasureText("0.00").X; // Camera Settings Widget _cameraWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); @@ -801,7 +801,7 @@ namespace FlaxEditor.Viewport #region View mode widget largestText = "Brightness"; - textSize = Style.Current.FontMedium.First().MeasureText(largestText); + textSize = Style.Current.FontMedium.MeasureText(largestText); xLocationForExtras = textSize.X + 5; var viewMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft); diff --git a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs index a58350ded..fe73048fd 100644 --- a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs +++ b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs @@ -163,8 +163,8 @@ namespace FlaxEditor.Viewport.Widgets { var style = Style.Current; - if (style != null && style.FontMedium.First()) - Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.First().MeasureText(_text).X, Icon.IsValid); + if (style != null && style.FontMedium) + Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.MeasureText(_text).X, Icon.IsValid); } } } diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index 63ad44f29..1a81a9421 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -41,7 +41,7 @@ namespace FlaxEditor.Windows var nameLabel = new Label(icon.Right + 10, icon.Top, 200, 34) { Text = "Flax Engine", - Font = new FontReference(Style.Current.FontTitle), + Font = new MultiFontReference(Style.Current.FontTitle), HorizontalAlignment = TextAlignment.Near, VerticalAlignment = TextAlignment.Center, Parent = this @@ -54,7 +54,7 @@ namespace FlaxEditor.Windows Parent = this }; var buttonText = "Copy version info"; - var fontSize = Style.Current.FontMedium.First().MeasureText(buttonText); + var fontSize = Style.Current.FontMedium.MeasureText(buttonText); var copyVersionButton = new Button(Width - fontSize.X - 8, 6, fontSize.X + 4, 20) { Text = buttonText, diff --git a/Source/Editor/Windows/Assets/FontWindow.cs b/Source/Editor/Windows/Assets/FontWindow.cs index ff4135165..2e2e14d99 100644 --- a/Source/Editor/Windows/Assets/FontWindow.cs +++ b/Source/Editor/Windows/Assets/FontWindow.cs @@ -144,7 +144,7 @@ namespace FlaxEditor.Windows.Assets protected override void OnAssetLinked() { Asset.WaitForLoaded(); - _textPreview.Font = new FontReference(Asset.CreateFont(30)); + _textPreview.Font = new MultiFontReference([Asset], 30); _inputText.Text = string.Format("This is a sample text using font {0}.", Asset.FamilyName); var options = Asset.Options; _proxy.Set(ref options); diff --git a/Source/Editor/Windows/ContentWindow.Search.cs b/Source/Editor/Windows/ContentWindow.Search.cs index a1072d158..f29dc0bd6 100644 --- a/Source/Editor/Windows/ContentWindow.Search.cs +++ b/Source/Editor/Windows/ContentWindow.Search.cs @@ -57,7 +57,7 @@ namespace FlaxEditor.Windows var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + Render2D.DrawText(Font.GetMultiFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); // Arrow diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index 6526d7c8a..ac4fea5ba 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -470,9 +470,9 @@ namespace FlaxEditor.Windows var wasEmpty = _output.TextLength == 0; // Cache fonts - _output.DefaultStyle.Font.GetFont(); - _output.WarningStyle.Font.GetFont(); - _output.ErrorStyle.Font.GetFont(); + _output.DefaultStyle.Font.GetMultiFont(); + _output.WarningStyle.Font.GetMultiFont(); + _output.ErrorStyle.Font.GetMultiFont(); // Generate the output log Span entries = CollectionsMarshal.AsSpan(_entries); @@ -536,7 +536,7 @@ namespace FlaxEditor.Windows } var prevBlockBottom = _textBlocks.Count == 0 ? 0.0f : _textBlocks[_textBlocks.Count - 1].Bounds.Bottom; var entryText = _textBuffer.ToString(startIndex, endIndex - startIndex); - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font) continue; var style = textBlock.Style; @@ -544,46 +544,52 @@ namespace FlaxEditor.Windows for (int j = 0; j < lines.Length; j++) { ref var line = ref lines[j]; - textBlock.Range.StartIndex = startIndex + line.FirstCharIndex; - textBlock.Range.EndIndex = startIndex + line.LastCharIndex + 1; - textBlock.Bounds = new Rectangle(new Float2(0.0f, prevBlockBottom), line.Size); - - if (textBlock.Range.Length > 0) + for (int k = 0; k < line.Blocks.Length; k++) { - // Parse compilation error/warning - var regexStart = line.FirstCharIndex; - if (j == 0) - regexStart += prefixLength; - var regexLength = line.LastCharIndex + 1 - regexStart; - if (regexLength > 0) + ref var block = ref line.Blocks[k]; + + textBlock.Range.StartIndex = startIndex + block.FirstCharIndex; + textBlock.Range.EndIndex = startIndex + block.LastCharIndex + 1; + textBlock.Bounds = new Rectangle(new Float2(block.Location.X, prevBlockBottom), block.Size); + + if (textBlock.Range.Length > 0) { - var match = _compileRegex.Match(entryText, regexStart, regexLength); - if (match.Success) + // Parse compilation error/warning + var regexStart = block.FirstCharIndex; + if (j == 0) + regexStart += prefixLength; + var regexLength = block.LastCharIndex + 1 - regexStart; + if (regexLength > 0) { - switch (match.Groups["level"].Value) + var match = _compileRegex.Match(entryText, regexStart, regexLength); + if (match.Success) { - case "error": - textBlock.Style = _output.ErrorStyle; - break; - case "warning": - textBlock.Style = _output.WarningStyle; - break; + switch (match.Groups["level"].Value) + { + case "error": + textBlock.Style = _output.ErrorStyle; + break; + case "warning": + textBlock.Style = _output.WarningStyle; + break; + } + textBlock.Tag = new TextBlockTag + { + Type = TextBlockTag.Types.CodeLocation, + Url = match.Groups["path"].Value, + Line = int.Parse(match.Groups["line"].Value), + }; } - textBlock.Tag = new TextBlockTag - { - Type = TextBlockTag.Types.CodeLocation, - Url = match.Groups["path"].Value, - Line = int.Parse(match.Groups["line"].Value), - }; + // TODO: parsing hyperlinks with link + // TODO: parsing file paths with link } - // TODO: parsing hyperlinks with link - // TODO: parsing file paths with link } + + _textBlocks.Add(textBlock); + textBlock.Style = style; } prevBlockBottom += line.Size.Y; - _textBlocks.Add(textBlock); - textBlock.Style = style; } } diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index 372505795..d6c14f4e4 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -14,6 +14,7 @@ using FlaxEditor.GUI.Tabs; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Json; +using FlaxEngine.Utilities; namespace FlaxEditor.Windows { @@ -78,7 +79,7 @@ namespace FlaxEditor.Windows HorizontalAlignment = TextAlignment.Near, AnchorPreset = AnchorPresets.HorizontalStretchTop, Text = desc.Name, - Font = new FontReference(Style.Current.FontLarge), + Font = new MultiFontReference(Style.Current.FontLarge), Parent = this, Bounds = new Rectangle(tmp1, margin, Width - tmp1 - margin, 28), }; @@ -119,8 +120,8 @@ namespace FlaxEditor.Windows url = desc.HomepageUrl; else if (!string.IsNullOrEmpty(desc.RepositoryUrl)) url = desc.RepositoryUrl; - versionLabel.Font.Font.WaitForLoaded(); - var font = versionLabel.Font.GetFont(); + versionLabel.Font.ForEach(x => x.Font.WaitForLoaded()); + var font = versionLabel.Font.GetMultiFont(); var authorWidth = font.MeasureText(desc.Author).X + 8; var authorLabel = new ClickableLabel { @@ -391,7 +392,7 @@ namespace FlaxEditor.Windows } Editor.Log("Plugin project has been cloned."); - + try { // Start git submodule clone diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index 88019b329..917647f23 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -85,8 +85,8 @@ namespace FlaxEditor.Windows.Profiler Render2D.FillRectangle(bounds, color); Render2D.DrawRectangle(bounds, color * 0.5f); - if (_nameLength < 0 && style.FontMedium.First()) - _nameLength = style.FontMedium.First().MeasureText(_name).X; + if (_nameLength < 0 && style.FontMedium) + _nameLength = style.FontMedium.MeasureText(_name).X; if (_nameLength < bounds.Width + 4) { diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs index e8fc1d56c..16b9165e1 100644 --- a/Source/Editor/Windows/ToolboxWindow.cs +++ b/Source/Editor/Windows/ToolboxWindow.cs @@ -273,8 +273,8 @@ namespace FlaxEditor.Windows var textRect = item.TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.First().GetCharPosition(text, ranges[i].StartIndex); - var end = font.First().GetCharPosition(text, ranges[i].EndIndex); + var start = font.GetCharPosition(text, ranges[i].StartIndex); + var end = font.GetCharPosition(text, ranges[i].EndIndex); highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } item.SetHighlights(highlights); diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index 277ad8ddd..b33b10899 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -293,249 +293,6 @@ void Font::ProcessText(const StringView& text, Array& outputLines } } -void Font::ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) -{ - float cursorX = 0; - int32 kerning; - MultiFontLineCache tmpLine; - MultiFontSegmentCache tmpSegment; - FontCharacterEntry entry; - FontCharacterEntry previous; - int32 textLength = text.Length(); - float scale = layout.Scale / FontManager::FontScale; - float boundsWidth = layout.Bounds.GetWidth(); - float baseLinesDistanceScale = layout.BaseLinesGapScale * scale; - - tmpSegment.Location = Float2::Zero; - tmpSegment.Height = 0; - tmpSegment.FirstCharIndex = 0; - tmpSegment.LastCharIndex = -1; - - tmpLine.Location = Float2::Zero; - tmpLine.Size = Float2::Zero; - tmpLine.Segments = Array(); - - if (textLength == 0) { - return; - } - - int32 lastWrapCharIndex = INVALID_INDEX; - float lastWrapCharX = 0; - bool lastMoveLine = false; - // The index of the font used by the current segment - int32 currentFontIndex = GetCharFontIndex(fonts, text[0], 0); - // The maximum font height of the current line - float maxHeight = 0; - float maxAscender = 0; - - // Process each character to split text into single lines - for (int32 currentIndex = 0; currentIndex < textLength;) - { - bool moveLine = false; - bool moveSegment = false; - float xAdvance = 0; - int32 nextCharIndex = currentIndex + 1; - - // Submit line and segment if text ends - if (nextCharIndex == textLength) { - moveLine = moveSegment = true; - } - - // Cache current character - const Char currentChar = text[currentIndex]; - const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - - // Check if character can wrap words - const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar) || (currentChar >= 0x3040 && currentChar <= 0x9FFF); - if (isWrapChar && currentIndex != 0) - { - lastWrapCharIndex = currentIndex; - lastWrapCharX = cursorX; - } - - int32 nextFontIndex = currentFontIndex; - // Check if it's a newline character - if (currentChar == '\n') - { - // Break line - moveLine = moveSegment = true; - tmpSegment.LastCharIndex++; - } - else - { - // Get character entry - if (nextCharIndex < textLength) { - nextFontIndex = GetCharFontIndex(fonts, text[nextCharIndex], currentFontIndex); - } - - // Get character entry - fonts[currentFontIndex]->GetCharacter(currentChar, entry); - maxHeight = Math::Max(maxHeight, static_cast(fonts[currentFontIndex]->GetHeight())); - maxAscender = Math::Max(maxAscender, static_cast(fonts[currentFontIndex]->GetAscender())); - - // Move segment if the font changes or text ends - if (nextFontIndex != currentFontIndex || nextCharIndex == textLength) { - moveSegment = true; - } - - // Get kerning, only when the font hasn't changed - if (!isWhitespace && previous.IsValid && !moveSegment) - { - kerning = fonts[currentFontIndex]->GetKerning(previous.Character, entry.Character); - } - else - { - kerning = 0; - } - previous = entry; - xAdvance = (kerning + entry.AdvanceX) * scale; - - // Check if character fits the line or skip wrapping - if (cursorX + xAdvance <= boundsWidth || layout.TextWrapping == TextWrapping::NoWrap) - { - // Move character - cursorX += xAdvance; - tmpSegment.LastCharIndex++; - } - else if (layout.TextWrapping == TextWrapping::WrapWords) - { - if (lastWrapCharIndex != INVALID_INDEX) - { - // Skip moving twice for the same character - int32 lastLineLastCharIndex = outputLines.HasItems() && outputLines.Last().Segments.HasItems() ? outputLines.Last().Segments.Last().LastCharIndex : -10000; - if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2) - { - currentIndex = nextCharIndex; - lastMoveLine = moveLine; - continue; - } - - // Move line - const Char wrapChar = text[lastWrapCharIndex]; - moveLine = true; - moveSegment = tmpSegment.FirstCharIndex < lastWrapCharIndex; - - cursorX = lastWrapCharX; - if (StringUtils::IsWhitespace(wrapChar)) - { - // Skip whitespaces - tmpSegment.LastCharIndex = lastWrapCharIndex - 1; - nextCharIndex = currentIndex = lastWrapCharIndex + 1; - } - else - { - tmpSegment.LastCharIndex = lastWrapCharIndex - 1; - nextCharIndex = currentIndex = lastWrapCharIndex; - } - } - } - else if (layout.TextWrapping == TextWrapping::WrapChars) - { - // Move line - moveLine = true; - moveSegment = tmpSegment.FirstCharIndex < currentChar; - nextCharIndex = currentIndex; - - // Skip moving twice for the same character - if (lastMoveLine) - break; - } - } - - if (moveSegment) { - // Add segment - tmpSegment.Height = baseLinesDistanceScale * fonts[currentFontIndex]->GetHeight(); - tmpSegment.LastCharIndex = Math::Max(tmpSegment.LastCharIndex, tmpSegment.FirstCharIndex); - tmpSegment.FontIndex = currentFontIndex; - tmpLine.Segments.Add(tmpSegment); - - // Reset segment - tmpSegment.Location.X = cursorX; - tmpSegment.FirstCharIndex = nextCharIndex; - tmpSegment.LastCharIndex = nextCharIndex - 1; - - currentFontIndex = nextFontIndex; - } - - // Check if move to another line - if (moveLine) - { - // Add line - tmpLine.Size.X = cursorX; - tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - tmpLine.MaxAscender = maxAscender; - outputLines.Add(tmpLine); - - // Reset line - tmpLine.Segments.Clear(); - tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; - cursorX = 0; - tmpSegment.Location.X = cursorX; - lastWrapCharIndex = INVALID_INDEX; - lastWrapCharX = 0; - previous.IsValid = false; - - // Reset max font height - maxHeight = 0; - maxAscender = 0; - } - - currentIndex = nextCharIndex; - lastMoveLine = moveLine; - } - - // Check if an additional line should be created - if (text[textLength - 1] == '\n') - { - // Add line - tmpLine.Size.X = cursorX; - tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - outputLines.Add(tmpLine); - - tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; - } - - // Check amount of lines - if (outputLines.IsEmpty()) - return; - - float totalHeight = tmpLine.Location.Y; - - Float2 offset = Float2::Zero; - if (layout.VerticalAlignment == TextAlignment::Center) - { - offset.Y += (layout.Bounds.GetHeight() - totalHeight) * 0.5f; - } - else if (layout.VerticalAlignment == TextAlignment::Far) - { - offset.Y += layout.Bounds.GetHeight() - totalHeight; - } - for (int32 i = 0; i < outputLines.Count(); i++) - { - MultiFontLineCache& line = outputLines[i]; - Float2 rootPos = line.Location + offset; - - // Fix upper left line corner to match desire text alignment - if (layout.HorizontalAlignment == TextAlignment::Center) - { - rootPos.X += (layout.Bounds.GetWidth() - line.Size.X) * 0.5f; - } - else if (layout.HorizontalAlignment == TextAlignment::Far) - { - rootPos.X += layout.Bounds.GetWidth() - line.Size.X; - } - - line.Location = rootPos; - - // Align all segments to center in case they have different heights - for (int32 j = 0; j < line.Segments.Count(); j++) - { - MultiFontSegmentCache& segment = line.Segments[j]; - segment.Location.Y += (line.MaxAscender - fonts[segment.FontIndex]->GetAscender()) / 2; - } - } -} - Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout) { // Check if there is no need to do anything @@ -643,7 +400,7 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo ASSERT(lines.HasItems()); float scale = layout.Scale / FontManager::FontScale; float baseLinesDistance = static_cast(_height) * layout.BaseLinesGapScale * scale; - Float2 rootOffset = layout.Bounds.Location + lines.First().Location; + Float2 rootOffset = layout.Bounds.Location; // Find line with that position FontCharacterEntry previous; @@ -682,10 +439,10 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo } // Position after last character in the last line - return rootOffset + Float2(lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); + return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); } -bool Font::ContainsChar(Char c) +bool Font::ContainsChar(Char c) const { return FT_Get_Char_Index(GetAsset()->GetFTFace(), c) > 0; } diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index 0425f2bc5..453872582 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -8,7 +8,6 @@ #include "Engine/Content/AssetReference.h" #include "Engine/Scripting/ScriptingObject.h" #include "TextLayoutOptions.h" -#include "MultiFont.h" class FontAsset; struct FontTextureAtlasSlot; @@ -341,15 +340,6 @@ public: /// The output lines list. void ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); - /// - /// Processes text to get cached lines for rendering. - /// - /// The font list. - /// The input text. - /// The layout properties. - /// The output lines list. - static void ProcessText(const Array& fonts, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); - /// /// Processes text to get cached lines for rendering. /// @@ -406,17 +396,6 @@ public: /// The minimum size for that text and fot to render properly. API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); - /* - /// - /// Measures minimum size of the rectangle that will be needed to draw given text. - /// - /// The fonts to render with. - /// The input text to test. - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() static Float2 MeasureText(const Array& fonts, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); - */ - /// /// Measures minimum size of the rectangle that will be needed to draw given text. /// @@ -504,18 +483,6 @@ public: /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); - /* - /// - /// Calculates character position for given text and character index. - /// - /// The fonts to use. - /// The input text to test. - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() static Float2 GetCharPosition(const Array& fonts, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); - */ - /// /// Calculates character position for given text and character index. /// @@ -557,28 +524,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); - - /// - /// Gets the index of the font that should be used to render the char - /// - /// The font list. - /// The char. - /// Number to return if char cannot be found. - /// - API_FUNCTION() FORCE_INLINE static int32 GetCharFontIndex(const Array& fonts, Char c, int32 missing = -1) { - int32 fontIndex = 0; - while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(c)) - { - fontIndex++; - } - - if (fontIndex == fonts.Count()) { - return missing; - } - - return fontIndex; - } + API_FUNCTION() FORCE_INLINE bool ContainsChar(Char c) const; /// /// Flushes the size of the face with the Free Type library backend. diff --git a/Source/Engine/Render2D/MultiFont.cpp b/Source/Engine/Render2D/MultiFont.cpp index 9844c12e0..4a510a9f8 100644 --- a/Source/Engine/Render2D/MultiFont.cpp +++ b/Source/Engine/Render2D/MultiFont.cpp @@ -1 +1,431 @@ #include "MultiFont.h" +#include "FontManager.h" +#include "Engine/Core/Math/Math.h" + +MultiFont::MultiFont(const Array& fonts) + : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)), + _fonts(fonts) +{ + +} + +void MultiFont::ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) +{ + float cursorX = 0; + int32 kerning; + MultiFontLineCache tmpLine; + MultiFontBlockCache tmpBlock; + FontCharacterEntry entry; + FontCharacterEntry previous; + int32 textLength = text.Length(); + float scale = layout.Scale / FontManager::FontScale; + float boundsWidth = layout.Bounds.GetWidth(); + float baseLinesDistanceScale = layout.BaseLinesGapScale * scale; + + tmpBlock.Location = Float2::Zero; + tmpBlock.Size = Float2::Zero; + tmpBlock.FirstCharIndex = 0; + tmpBlock.LastCharIndex = -1; + + tmpLine.Location = Float2::Zero; + tmpLine.Size = Float2::Zero; + tmpLine.Blocks = Array(); + + if (textLength == 0) { + return; + } + + int32 lastWrapCharIndex = INVALID_INDEX; + float lastWrapCharX = 0; + bool lastMoveLine = false; + // The index of the font used by the current block + int32 currentFontIndex = GetCharFontIndex(text[0], 0); + // The maximum font height of the current line + float maxHeight = 0; + float maxAscender = 0; + float lastCursorX = 0; + + // Process each character to split text into single lines + for (int32 currentIndex = 0; currentIndex < textLength;) + { + bool moveLine = false; + bool moveBlock = false; + float xAdvance = 0; + int32 nextCharIndex = currentIndex + 1; + + // Submit line and block if text ends + if (nextCharIndex == textLength) { + moveLine = moveBlock = true; + } + + // Cache current character + const Char currentChar = text[currentIndex]; + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); + + // Check if character can wrap words + const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar) || (currentChar >= 0x3040 && currentChar <= 0x9FFF); + if (isWrapChar && currentIndex != 0) + { + lastWrapCharIndex = currentIndex; + lastWrapCharX = cursorX; + } + + int32 nextFontIndex = currentFontIndex; + // Check if it's a newline character + if (currentChar == '\n') + { + // Break line + moveLine = moveBlock = true; + tmpBlock.LastCharIndex++; + } + else + { + // Get character entry + if (nextCharIndex < textLength) { + nextFontIndex = GetCharFontIndex(text[nextCharIndex], currentFontIndex); + } + + // Get character entry + _fonts[currentFontIndex]->GetCharacter(currentChar, entry); + maxHeight = Math::Max(maxHeight, static_cast(_fonts[currentFontIndex]->GetHeight())); + maxAscender = Math::Max(maxAscender, static_cast(_fonts[currentFontIndex]->GetAscender())); + + // Move block if the font changes or text ends + if (nextFontIndex != currentFontIndex || nextCharIndex == textLength) { + moveBlock = true; + } + + // Get kerning, only when the font hasn't changed + if (!isWhitespace && previous.IsValid && !moveBlock) + { + kerning = _fonts[currentFontIndex]->GetKerning(previous.Character, entry.Character); + } + else + { + kerning = 0; + } + previous = entry; + xAdvance = (kerning + entry.AdvanceX) * scale; + + // Check if character fits the line or skip wrapping + if (cursorX + xAdvance <= boundsWidth || layout.TextWrapping == TextWrapping::NoWrap) + { + // Move character + cursorX += xAdvance; + tmpBlock.LastCharIndex++; + } + else if (layout.TextWrapping == TextWrapping::WrapWords) + { + if (lastWrapCharIndex != INVALID_INDEX) + { + // Skip moving twice for the same character + int32 lastLineLastCharIndex = outputLines.HasItems() && outputLines.Last().Blocks.HasItems() ? outputLines.Last().Blocks.Last().LastCharIndex : -10000; + if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2) + { + currentIndex = nextCharIndex; + lastMoveLine = moveLine; + continue; + } + + // Move line + const Char wrapChar = text[lastWrapCharIndex]; + moveLine = true; + moveBlock = tmpBlock.FirstCharIndex < lastWrapCharIndex; + + cursorX = lastWrapCharX; + if (StringUtils::IsWhitespace(wrapChar)) + { + // Skip whitespaces + tmpBlock.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex + 1; + } + else + { + tmpBlock.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex; + } + } + } + else if (layout.TextWrapping == TextWrapping::WrapChars) + { + // Move line + moveLine = true; + moveBlock = tmpBlock.FirstCharIndex < currentChar; + nextCharIndex = currentIndex; + + // Skip moving twice for the same character + if (lastMoveLine) + break; + } + } + + if (moveBlock) { + // Add block + tmpBlock.Size.X = lastCursorX - cursorX; + tmpBlock.Size.Y = baseLinesDistanceScale * _fonts[currentFontIndex]->GetHeight(); + tmpBlock.LastCharIndex = Math::Max(tmpBlock.LastCharIndex, tmpBlock.FirstCharIndex); + tmpBlock.FontIndex = currentFontIndex; + tmpLine.Blocks.Add(tmpBlock); + + // Reset block + tmpBlock.Location.X = cursorX; + tmpBlock.FirstCharIndex = nextCharIndex; + tmpBlock.LastCharIndex = nextCharIndex - 1; + + currentFontIndex = nextFontIndex; + lastCursorX = cursorX; + } + + // Check if move to another line + if (moveLine) + { + // Add line + tmpLine.Size.X = cursorX; + tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; + tmpLine.MaxAscender = maxAscender; + outputLines.Add(tmpLine); + + // Reset line + tmpLine.Blocks.Clear(); + tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; + cursorX = 0; + tmpBlock.Location.X = cursorX; + lastWrapCharIndex = INVALID_INDEX; + lastWrapCharX = 0; + previous.IsValid = false; + + // Reset max font height + maxHeight = 0; + maxAscender = 0; + lastCursorX = 0; + } + + currentIndex = nextCharIndex; + lastMoveLine = moveLine; + } + + // Check if an additional line should be created + if (text[textLength - 1] == '\n') + { + // Add line + tmpLine.Size.X = cursorX; + tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; + outputLines.Add(tmpLine); + + tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; + } + + // Check amount of lines + if (outputLines.IsEmpty()) + return; + + float totalHeight = tmpLine.Location.Y; + + Float2 offset = Float2::Zero; + if (layout.VerticalAlignment == TextAlignment::Center) + { + offset.Y += (layout.Bounds.GetHeight() - totalHeight) * 0.5f; + } + else if (layout.VerticalAlignment == TextAlignment::Far) + { + offset.Y += layout.Bounds.GetHeight() - totalHeight; + } + for (int32 i = 0; i < outputLines.Count(); i++) + { + MultiFontLineCache& line = outputLines[i]; + Float2 rootPos = line.Location + offset; + + // Fix upper left line corner to match desire text alignment + if (layout.HorizontalAlignment == TextAlignment::Center) + { + rootPos.X += (layout.Bounds.GetWidth() - line.Size.X) * 0.5f; + } + else if (layout.HorizontalAlignment == TextAlignment::Far) + { + rootPos.X += layout.Bounds.GetWidth() - line.Size.X; + } + + line.Location = rootPos; + + // Align all blocks to center in case they have different heights + for (int32 j = 0; j < line.Blocks.Count(); j++) + { + MultiFontBlockCache& block = line.Blocks[j]; + block.Location.Y += (line.MaxAscender - _fonts[block.FontIndex]->GetAscender()) / 2; + } + } +} + +Float2 MultiFont::GetCharPosition(const StringView& text, int32 index, const TextLayoutOptions& layout) +{ + // Check if there is no need to do anything + if (text.IsEmpty()) + return layout.Bounds.Location; + + // Process text + Array lines; + ProcessText(text, lines, layout); + ASSERT(lines.HasItems()); + float scale = layout.Scale / FontManager::FontScale; + float baseLinesDistance = layout.BaseLinesGapScale * scale; + Float2 rootOffset = layout.Bounds.Location; + + // Find line with that position + FontCharacterEntry previous; + FontCharacterEntry entry; + for (int32 lineIndex = 0; lineIndex < lines.Count(); lineIndex++) + { + const MultiFontLineCache& line = lines[lineIndex]; + for (int32 blockIndex = 0; blockIndex < line.Blocks.Count(); blockIndex++) + { + const MultiFontBlockCache& block = line.Blocks[blockIndex]; + // Check if desire position is somewhere inside characters in line range + if (Math::IsInRange(index, block.FirstCharIndex, block.LastCharIndex)) + { + float x = line.Location.X + block.Location.X; + float y = line.Location.Y + block.Location.Y; + + // Check all characters in the line + for (int32 currentIndex = block.FirstCharIndex; currentIndex < index; currentIndex++) + { + // Cache current character + const Char currentChar = text[currentIndex]; + _fonts[block.FontIndex]->GetCharacter(currentChar, entry); + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); + + // Apply kerning + if (!isWhitespace && previous.IsValid) + { + x += _fonts[block.FontIndex]->GetKerning(previous.Character, entry.Character); + } + previous = entry; + + // Move + x += entry.AdvanceX * scale; + } + + // Upper left corner of the character + return rootOffset + Float2(x, y); + } + } + } + + // Position after last character in the last line + return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, lines.Last().Location.Y); +} + +int32 MultiFont::HitTestText(const StringView& text, const Float2& location, const TextLayoutOptions& layout) +{ + // Check if there is no need to do anything + if (text.Length() <= 0) + return 0; + + // Process text + Array lines; + ProcessText(text, lines, layout); + ASSERT(lines.HasItems()); + float scale = layout.Scale / FontManager::FontScale; + + // Offset position to match lines origin space + Float2 rootOffset = layout.Bounds.Location + lines.First().Location; + Float2 testPoint = location - rootOffset; + + // Get block which may intersect with the position (it's possible because lines have fixed height) + int32 lineIndex = 0; + while (lineIndex < lines.Count()) + { + if (lines[lineIndex].Location.Y + lines[lineIndex].Size.Y >= location.Y) { + break; + } + + lineIndex++; + } + lineIndex = Math::Clamp(lineIndex, 0, lines.Count() - 1); + const MultiFontLineCache& line = lines[lineIndex]; + + int32 blockIndex = 0; + while (blockIndex < line.Blocks.Count() - 1) + { + if (line.Location.X + line.Blocks[blockIndex + 1].Location.X >= location.X) { + break; + } + + blockIndex++; + } + const MultiFontBlockCache& block = line.Blocks[blockIndex]; + float x = line.Location.X; + + // Check all characters in the line to find hit point + FontCharacterEntry previous; + FontCharacterEntry entry; + int32 smallestIndex = INVALID_INDEX; + float dst, smallestDst = MAX_float; + for (int32 currentIndex = block.FirstCharIndex; currentIndex <= block.LastCharIndex; currentIndex++) + { + // Cache current character + const Char currentChar = text[currentIndex]; + + _fonts[block.FontIndex]->GetCharacter(currentChar, entry); + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); + + // Apply kerning + if (!isWhitespace && previous.IsValid) + { + x += _fonts[block.FontIndex]->GetKerning(previous.Character, entry.Character); + } + previous = entry; + + // Test + dst = Math::Abs(testPoint.X - x); + if (dst < smallestDst) + { + // Found closer character + smallestIndex = currentIndex; + smallestDst = dst; + } + else if (dst > smallestDst) + { + // Current char is worse so return the best result + return smallestIndex; + } + + // Move + x += entry.AdvanceX * scale; + } + + // Test line end edge + dst = Math::Abs(testPoint.X - x); + if (dst < smallestDst) + { + // Pointer is behind the last character in the line + smallestIndex = block.LastCharIndex; + + // Fix for last line + if (lineIndex == lines.Count() - 1) + smallestIndex++; + } + + return smallestIndex; +} + +Float2 MultiFont::MeasureText(const StringView& text, const TextLayoutOptions& layout) +{ + // Check if there is no need to do anything + if (text.IsEmpty()) + return Float2::Zero; + + // Process text + Array lines; + ProcessText(text, lines, layout); + + // Calculate bounds + Float2 max = Float2::Zero; + for (int32 i = 0; i < lines.Count(); i++) + { + const MultiFontLineCache& line = lines[i]; + max = Float2::Max(max, line.Location + line.Size); + } + + return max; +} + diff --git a/Source/Engine/Render2D/MultiFont.h b/Source/Engine/Render2D/MultiFont.h index b1da832d5..870b8e668 100644 --- a/Source/Engine/Render2D/MultiFont.h +++ b/Source/Engine/Render2D/MultiFont.h @@ -3,23 +3,28 @@ #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/Dictionary.h" #include "Font.h" +#include "FontAsset.h" + +struct TextRange; +class Font; +class FontAsset; /// -/// The font segment info generated during text processing. +/// The font block info generated during text processing. /// -API_STRUCT(NoDefault) struct MultiFontSegmentCache +API_STRUCT(NoDefault) struct MultiFontBlockCache { - DECLARE_SCRIPTING_TYPE_MINIMAL(MultiFontSegmentCache); + DECLARE_SCRIPTING_TYPE_MINIMAL(MultiFontBlockCache); /// - /// The root position of the segment (upper left corner), relative to line. + /// The root position of the block (upper left corner), relative to line. /// API_FIELD() Float2 Location; /// - /// The height of the current segment + /// The height of the current block /// - API_FIELD() float Height; + API_FIELD() Float2 Size; /// /// The first character index (from the input text). @@ -38,13 +43,13 @@ API_STRUCT(NoDefault) struct MultiFontSegmentCache }; template<> -struct TIsPODType +struct TIsPODType { enum { Value = true }; }; /// -/// Line of font segments info generated during text processing. +/// Line of font blocks info generated during text processing. /// API_STRUCT(NoDefault) struct MultiFontLineCache { @@ -61,17 +66,288 @@ API_STRUCT(NoDefault) struct MultiFontLineCache API_FIELD() Float2 Size; /// - /// The maximum ascendent of the line. + /// The maximum ascender of the line. /// API_FIELD() float MaxAscender; /// /// The index of the font to render with /// - API_FIELD() Array Segments; + API_FIELD() Array Blocks; }; API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API MultiFont : public ManagedScriptingObject { DECLARE_SCRIPTING_TYPE_NO_SPAWN(MultiFont); +private: + Array _fonts; + +public: + MultiFont(const Array& fonts); + + API_FUNCTION() FORCE_INLINE static MultiFont* Create(const Array& fonts) { + return New(fonts); + } + + API_FUNCTION() FORCE_INLINE static MultiFont* Create(const Array& fontAssets, float size) { + Array fonts; + fonts.Resize(fontAssets.Count()); + for (int32 i = 0; i < fontAssets.Count(); i++) + { + fonts[i] = fontAssets[i]->CreateFont(size); + } + + return New(fonts); + } + + API_PROPERTY() FORCE_INLINE Array& GetFonts() { + return _fonts; + } + + API_PROPERTY() FORCE_INLINE void SetFonts(const Array& val) { + _fonts = val; + } + + API_PROPERTY() FORCE_INLINE int32 GetMaxHeight() { + int32 maxHeight = 0; + for (int32 i = 0; i < _fonts.Count(); i++) + { + if (_fonts[i]) { + maxHeight = Math::Max(maxHeight, _fonts[i]->GetHeight()); + } + } + + return maxHeight; + } + + API_PROPERTY() FORCE_INLINE int32 GetMaxAscender() { + int32 maxAsc = 0; + for (int32 i = 0; i < _fonts.Count(); i++) + { + if (_fonts[i]) { + maxAsc = Math::Max(maxAsc, _fonts[i]->GetAscender()); + } + } + + return maxAsc; + } + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The layout properties. + /// The output lines list. + void ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The layout properties. + /// The output lines list. + API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) + { + Array lines; + ProcessText(text, lines, layout); + return lines; + } + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The input text range (substring range of the input text parameter). + /// The layout properties. + /// The output lines list. + API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + { + Array lines; + ProcessText(textRange.Substring(text), lines, layout); + return lines; + } + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The output lines list. + API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text) + { + return ProcessText(text, TextLayoutOptions()); + } + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The input text range (substring range of the input text parameter). + /// The output lines list. + API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange) + { + return ProcessText(textRange.Substring(text), TextLayoutOptions()); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text. + /// + /// The input text to test. + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return MeasureText(textRange.Substring(text), layout); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text + /// . + /// The input text to test. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text) + { + return MeasureText(text, TextLayoutOptions()); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text + /// . + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange) + { + return MeasureText(textRange.Substring(text), TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return HitTestText(textRange.Substring(text), location, layout); + } + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, const Float2& location) + { + return HitTestText(text, location, TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) + { + return HitTestText(textRange.Substring(text), location, TextLayoutOptions()); + } + + /// + /// Calculates character position for given text and character index. + /// + /// The input text to test. + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Calculates character position for given text and character index. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return GetCharPosition(textRange.Substring(text), index, layout); + } + + /// + /// Calculates character position for given text and character index + /// + /// The input text to test. + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, int32 index) + { + return GetCharPosition(text, index, TextLayoutOptions()); + } + + /// + /// Calculates character position for given text and character index + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) + { + return GetCharPosition(textRange.Substring(text), index, TextLayoutOptions()); + } + + /// + /// Gets the index of the font that should be used to render the char + /// + /// The font list. + /// The char. + /// Number to return if char cannot be found. + /// + API_FUNCTION() FORCE_INLINE int32 GetCharFontIndex(Char c, int32 missing = -1) { + int32 fontIndex = 0; + while (fontIndex < _fonts.Count() && _fonts[fontIndex] && !_fonts[fontIndex]->ContainsChar(c)) + { + fontIndex++; + } + + if (fontIndex == _fonts.Count()) { + return missing; + } + + return fontIndex; + } + + API_FUNCTION() FORCE_INLINE bool Verify() { + for (int32 i = 0; i < _fonts.Count(); i++) + { + if (!_fonts[i]) { + return false; + } + } + + return true; + } }; diff --git a/Source/Engine/Render2D/MultiFontReference.cs b/Source/Engine/Render2D/MultiFontReference.cs new file mode 100644 index 000000000..dd3f4d179 --- /dev/null +++ b/Source/Engine/Render2D/MultiFontReference.cs @@ -0,0 +1,74 @@ + + +using System.Collections.Generic; +using System.Linq; + +namespace FlaxEngine +{ + /// + /// Reference to multiple font references + /// + public class MultiFontReference : List + { + public MultiFontReference() + { + _cachedFont = null; + } + + public MultiFontReference(IEnumerable other) + { + AddRange(other); + _cachedFont = null; + } + + public MultiFontReference(MultiFontReference other) + { + AddRange(other); + _cachedFont = other._cachedFont; + } + + public MultiFontReference(MultiFontReference other, float size) + { + AddRange(other.Select(x => new FontReference(x) { Size = size })); + _cachedFont = null; + } + + public MultiFontReference(MultiFont other) + { + AddRange(other.Fonts.Select(x => new FontReference(x))); + _cachedFont = other; + } + + public MultiFontReference(FontAsset[] assets, float size) + { + AddRange(assets.Select(x => new FontReference(x, size))); + _cachedFont = null; + } + + [EditorOrder(0), Tooltip("The font asset to use as characters source.")] + public MultiFont GetMultiFont() + { + if (_cachedFont) + return _cachedFont; + var fontList = this.Where(x => x.Font).Select(x => x.GetFont()).ToArray(); + _cachedFont = MultiFont.Create(fontList); + return _cachedFont; + } + + public bool Verify() + { + foreach (var i in this) + { + if (!i.Font) + { + return false; + } + } + + return true; + } + + [NoSerialize] + private MultiFont _cachedFont; + } +} diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 8a39d29be..c68c2445d 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -1370,10 +1370,11 @@ void Render2D::DrawText(Font* font, const StringView& text, const TextRange& tex DrawText(font, textRange.Substring(text), color, layout, customMaterial); } -void Render2D::DrawText(const Array& fonts, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; + const Array& fonts = multiFont->GetFonts(); // Check if there is no need to do anything if (fonts.IsEmpty() || text.Length() < 0) return; @@ -1408,7 +1409,7 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) { if (text[currentIndex] != '\n') { - int32 fontIndex = Font::GetCharFontIndex(fonts, text[currentIndex], 0); + int32 fontIndex = multiFont->GetCharFontIndex(text[currentIndex], 0); maxAscenders[lineIndex] = Math::Max(maxAscenders[lineIndex], static_cast(fonts[fontIndex]->GetAscender())); } else { @@ -1418,12 +1419,12 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const } lineIndex = 0; - // The following code cut the text into segments, according to the font used to render + // The following code cut the text into blocks, according to the font used to render Float2 pointer = location; - // The starting index of the current segment + // The starting index of the current block int32 startIndex = 0; - // The index of the font used by the current segment - int32 currentFontIndex = Font::GetCharFontIndex(fonts, text[0], 0); + // The index of the font used by the current block + int32 currentFontIndex = multiFont->GetCharFontIndex(text[0], 0); // The maximum font height of the current line float maxHeight = 0; for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) @@ -1431,13 +1432,13 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const // Cache current character const Char currentChar = text[currentIndex]; int32 nextCharIndex = currentIndex + 1; - bool moveSegment = false; + bool moveBlock = false; bool moveLine = false; int32 nextFontIndex = currentFontIndex; - // Submit segment if text ends + // Submit block if text ends if (nextCharIndex == text.Length()) { - moveSegment = true; + moveBlock = true; } // Check if it isn't a newline character @@ -1445,21 +1446,21 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const { // Get character entry if (nextCharIndex < text.Length()) { - nextFontIndex = Font::GetCharFontIndex(fonts, text[nextCharIndex], currentFontIndex); + nextFontIndex = multiFont->GetCharFontIndex(text[nextCharIndex], currentFontIndex); } if (nextFontIndex != currentFontIndex) { - moveSegment = true; + moveBlock = true; } } else { // Move - moveLine = moveSegment = true; + moveLine = moveBlock = true; } - if (moveSegment) { - // Render the pending segment before beginning the new segment + if (moveBlock) { + // Render the pending block before beginning the new block auto fontHeight = fonts[currentFontIndex]->GetHeight(); maxHeight = Math::Max(maxHeight, static_cast(fontHeight)); auto fontDescender = fonts[currentFontIndex]->GetDescender(); @@ -1533,22 +1534,23 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const lineIndex++; } - // Start new segment + // Start new block startIndex = nextCharIndex; currentFontIndex = nextFontIndex; } } } -void Render2D::DrawText(const Array& fonts, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) { - DrawText(fonts, textRange.Substring(text), color, location, customMaterial); + DrawText(multiFont, textRange.Substring(text), color, location, customMaterial); } -void Render2D::DrawText(const Array& fonts, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; + const Array& fonts = multiFont->GetFonts(); // Check if there is no need to do anything if (fonts.IsEmpty() || text.IsEmpty() || layout.Scale <= ZeroTolerance) return; @@ -1563,7 +1565,7 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const // Process text to get lines MultiFontLines.Clear(); - Font::ProcessText(fonts, text, MultiFontLines, layout); + multiFont->ProcessText(text, MultiFontLines, layout); // Render all lines FontCharacterEntry entry; @@ -1582,14 +1584,14 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const for (int32 lineIndex = 0; lineIndex < MultiFontLines.Count(); lineIndex++) { const MultiFontLineCache& line = MultiFontLines[lineIndex]; - for (int32 segmentIndex = 0; segmentIndex < line.Segments.Count(); segmentIndex++) + for (int32 blockIndex = 0; blockIndex < line.Blocks.Count(); blockIndex++) { - const MultiFontSegmentCache& segment = MultiFontLines[lineIndex].Segments[segmentIndex]; - auto fontHeight = fonts[segment.FontIndex]->GetHeight(); - auto fontDescender = fonts[segment.FontIndex]->GetDescender(); - Float2 pointer = line.Location + segment.Location; + const MultiFontBlockCache& block = MultiFontLines[lineIndex].Blocks[blockIndex]; + auto fontHeight = fonts[block.FontIndex]->GetHeight(); + auto fontDescender = fonts[block.FontIndex]->GetDescender(); + Float2 pointer = line.Location + block.Location; - for (int32 charIndex = segment.FirstCharIndex; charIndex <= segment.LastCharIndex; charIndex++) + for (int32 charIndex = block.FirstCharIndex; charIndex <= block.LastCharIndex; charIndex++) { Char c = text[charIndex]; if (c == '\n') @@ -1598,7 +1600,7 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const } // Get character entry - fonts[segment.FontIndex]->GetCharacter(c, entry); + fonts[block.FontIndex]->GetCharacter(c, entry); // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) @@ -1623,7 +1625,7 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const const bool isWhitespace = StringUtils::IsWhitespace(c); if (!isWhitespace && previous.IsValid) { - kerning = fonts[segment.FontIndex]->GetKerning(previous.Character, entry.Character); + kerning = fonts[block.FontIndex]->GetKerning(previous.Character, entry.Character); } else { @@ -1659,9 +1661,9 @@ void Render2D::DrawText(const Array& fonts, const StringView& text, const } } -void Render2D::DrawText(const Array& fonts, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { - DrawText(fonts, textRange.Substring(text), color, layout, customMaterial); + DrawText(multiFont, textRange.Substring(text), color, layout, customMaterial); } FORCE_INLINE bool NeedAlphaWithTint(const Color& color) @@ -2165,22 +2167,22 @@ void Render2D::DrawBezier(const Float2& p1, const Float2& p2, const Float2& p3, { RENDER2D_CHECK_RENDERING_STATE; - // Find amount of segments to use + // Find amount of blocks to use const Float2 d1 = p2 - p1; const Float2 d2 = p3 - p2; const Float2 d3 = p4 - p3; const float len = d1.Length() + d2.Length() + d3.Length(); - const int32 segmentCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100); - const float segmentCountInv = 1.0f / segmentCount; + const int32 blockCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100); + const float blockCountInv = 1.0f / blockCount; - // Draw segmented curve + // Draw blocked curve Float2 p; AnimationUtils::Bezier(p1, p2, p3, p4, 0, p); Lines2.Clear(); Lines2.Add(p); - for (int32 i = 1; i <= segmentCount; i++) + for (int32 i = 1; i <= blockCount; i++) { - const float t = i * segmentCountInv; + const float t = i * blockCountInv; AnimationUtils::Bezier(p1, p2, p3, p4, t, p); Lines2.Add(p); } diff --git a/Source/Engine/Render2D/Render2D.cs b/Source/Engine/Render2D/Render2D.cs index a73556f7b..cad8380ab 100644 --- a/Source/Engine/Render2D/Render2D.cs +++ b/Source/Engine/Render2D/Render2D.cs @@ -164,7 +164,7 @@ namespace FlaxEngine /// Describes how wrap text inside a layout rectangle. /// The scale for distance one baseline from another. Default is 1. /// The text drawing scale. Default is 1. - public static void DrawText(Font[] fonts, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + public static void DrawText(MultiFont fonts, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) { var layout = new TextLayoutOptions { @@ -175,6 +175,8 @@ namespace FlaxEngine Scale = scale, BaseLinesGapScale = baseLinesGapScale, }; + + DrawText(fonts, text, color, ref layout); } @@ -191,7 +193,7 @@ namespace FlaxEngine /// Describes how wrap text inside a layout rectangle. /// The scale for distance one baseline from another. Default is 1. /// The text drawing scale. Default is 1. - public static void DrawText(Font[] fonts, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + public static void DrawText(MultiFont fonts, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) { var layout = new TextLayoutOptions { @@ -202,6 +204,7 @@ namespace FlaxEngine Scale = scale, BaseLinesGapScale = baseLinesGapScale, }; + DrawText(fonts, text, color, ref layout, customMaterial); } diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index 0ec88edc0..5050171b2 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -15,6 +15,7 @@ struct Matrix3x3; struct Viewport; struct TextRange; class Font; +class MultiFont; class GPUPipelineState; class GPUTexture; class GPUTextureView; @@ -224,7 +225,7 @@ public: /// The text color. /// The text location. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// /// Draws a text with formatting. @@ -234,7 +235,7 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// /// Draws a text with formatting. @@ -245,7 +246,7 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// /// Draws a text with formatting. @@ -256,7 +257,7 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(const Array& fonts, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// /// Fills a rectangle area. diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index 8e71c9f31..bc800f88a 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -294,15 +294,12 @@ namespace FlaxEngine style.DragWindow = style.BackgroundSelected * 0.7f; // Use optionally bundled default font (matches Editor) - var defaultFont = Content.LoadAsyncInternal("Editor/Fonts/Roboto-Regular"); - var cjkFont = Content.LoadAsyncInternal("NotoSansSC-Medium"); - if (defaultFont) - { - style.FontTitle = defaultFont.CreateFont(18); - style.FontLarge = defaultFont.CreateFont(14); - style.FontMedium = new Font[] { defaultFont.CreateFont(9), cjkFont.CreateFont(9) }; - style.FontSmall = new Font[] { defaultFont.CreateFont(9), cjkFont.CreateFont(9) }; - } + FontAsset[] defaultFont = [Content.LoadAsyncInternal("Editor/Fonts/Roboto-Regular"), Content.LoadAsyncInternal("Editor/Fonts/NotoSansSC-Regular")]; + + style.FontTitle = new MultiFontReference(defaultFont, 18).GetMultiFont(); + style.FontLarge = new MultiFontReference(defaultFont, 14).GetMultiFont(); + style.FontMedium = new MultiFontReference(defaultFont, 9).GetMultiFont(); + style.FontSmall = new MultiFontReference(defaultFont, 9).GetMultiFont(); Style.Current = style; } diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index 1b965b8d1..f21a29191 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -23,7 +23,7 @@ namespace FlaxEngine.GUI /// /// The font. /// - protected FontReference _font; + protected MultiFontReference _font; /// /// The text. @@ -44,7 +44,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font used to draw button text. /// [EditorDisplay("Text Style"), EditorOrder(2022), ExpandGroups] - public FontReference Font + public MultiFontReference Font { get => _font; set => _font = value; @@ -156,7 +156,7 @@ namespace FlaxEngine.GUI var style = Style.Current; if (style != null) { - _font = new FontReference(style.FontMedium.First()); + _font = new MultiFontReference(style.FontMedium); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BorderColor = style.BorderNormal; @@ -262,7 +262,7 @@ namespace FlaxEngine.GUI Render2D.DrawRectangle(clientRect, borderColor, BorderThickness); // Draw text - Render2D.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(_font?.GetMultiFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); } /// diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 21ca9cbea..2681aac1b 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -278,7 +278,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font used to draw text. /// [EditorDisplay("Text Style"), EditorOrder(2021)] - public FontReference Font { get; set; } + public MultiFontReference Font { get; set; } /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. @@ -359,7 +359,7 @@ namespace FlaxEngine.GUI : base(0, 0, 120, 18.0f) { var style = Style.Current; - Font = new FontReference(style.FontMedium.First()); + Font = new MultiFontReference(style.FontMedium); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BackgroundColorHighlighted = BackgroundColor; @@ -674,7 +674,7 @@ namespace FlaxEngine.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(Font.GetMultiFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); } diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index 5fa8ad21d..ccc1cc190 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -26,7 +26,7 @@ namespace FlaxEngine.GUI /// /// The font. /// - protected FontReference _font; + protected MultiFontReference _font; /// /// Gets or sets the text. @@ -86,7 +86,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font. /// [EditorDisplay("Text Style"), EditorOrder(2024)] - public FontReference Font + public MultiFontReference Font { get => _font; set @@ -192,7 +192,7 @@ namespace FlaxEngine.GUI { AutoFocus = false; var style = Style.Current; - Font = new FontReference(style.FontMedium.First()); + Font = new MultiFontReference(style.FontMedium); TextColor = style.Foreground; TextColorHighlighted = style.Foreground; } @@ -203,7 +203,7 @@ namespace FlaxEngine.GUI { AutoFocus = false; var style = Style.Current; - Font = new FontReference(style.FontMedium.First()); + Font = new MultiFontReference(style.FontMedium); TextColor = style.Foreground; TextColorHighlighted = style.Foreground; } @@ -235,7 +235,7 @@ namespace FlaxEngine.GUI } } - Render2D.DrawText([_font.GetFont(), Style.Current.FontCJK], Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + Render2D.DrawText(_font.GetMultiFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); if (ClipText) Render2D.PopClip(); @@ -246,7 +246,7 @@ namespace FlaxEngine.GUI { if (_autoWidth || _autoHeight || _autoFitText) { - var font = _font.GetFont(); + var font = _font.GetMultiFont(); if (font) { // Calculate text size diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs index 2270136ae..9523ad30a 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System; using System.Collections.Generic; using System.Runtime.InteropServices; using FlaxEngine.Utilities; @@ -185,7 +186,7 @@ namespace FlaxEngine.GUI }; // Process text into text blocks (handle newlines etc.) - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font) return; var lines = font.ProcessText(_text, ref textBlock.Range); @@ -194,20 +195,27 @@ namespace FlaxEngine.GUI for (int i = 0; i < lines.Length; i++) { ref var line = ref lines[i]; - textBlock.Range = new TextRange - { - StartIndex = start + line.FirstCharIndex, - EndIndex = start + line.LastCharIndex, - }; + if (i != 0) { context.Caret.X = 0; OnLineAdded(ref context, textBlock.Range.StartIndex - 1); } - textBlock.Bounds = new Rectangle(context.Caret, line.Size); - textBlock.Bounds.X += line.Location.X; + for (int k = 0; k < line.Blocks.Length; k++) + { + ref var block = ref line.Blocks[k]; - context.AddTextBlock(ref textBlock); + textBlock.Range = new TextRange + { + StartIndex = start + block.FirstCharIndex, + EndIndex = start + block.LastCharIndex, + }; + + textBlock.Bounds = new Rectangle(context.Caret, block.Size); + textBlock.Bounds.X += block.Location.X; + + context.AddTextBlock(ref textBlock); + } } // Update the caret location @@ -236,9 +244,9 @@ namespace FlaxEngine.GUI var ascender = textBlock.Ascender; //if (ascender <= 0) { - var textBlockFont = textBlock.Style.Font.GetFont(); + var textBlockFont = textBlock.Style.Font.GetMultiFont(); if (textBlockFont) - ascender = textBlockFont.Ascender; + ascender = textBlockFont.MaxAscender; } lineAscender = Mathf.Max(lineAscender, ascender); lineSize = Float2.Max(lineSize, textBlockSize); @@ -259,9 +267,9 @@ namespace FlaxEngine.GUI var ascender = textBlock.Ascender; if (ascender <= 0) { - var textBlockFont = textBlock.Style.Font.GetFont(); + var textBlockFont = textBlock.Style.Font.GetMultiFont(); if (textBlockFont) - ascender = textBlockFont.Ascender; + ascender = textBlockFont.MaxAscender; } vOffset = lineAscender - ascender; textBlock.Bounds.Location.Y += vOffset; diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs index 044c65044..08637918d 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using FlaxEngine.Utilities; +using System.Linq; namespace FlaxEngine.GUI { @@ -10,9 +11,9 @@ namespace FlaxEngine.GUI { context.Caret.X = 0; var style = context.StyleStack.Peek(); - var font = style.Font.GetFont(); + var font = style.Font.GetMultiFont(); if (font) - context.Caret.Y += font.Height; + context.Caret.Y += font.MaxHeight; } private static void ProcessColor(ref ParsingContext context, ref HtmlTag tag) @@ -86,15 +87,14 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - style.Font = new FontReference(style.Font); - if (tag.Attributes.TryGetValue(string.Empty, out var fontName)) + style.Font = new MultiFontReference(style.Font); + if (tag.Attributes.TryGetValue("size", out var sizeText) && int.TryParse(sizeText, out var size) && tag.Attributes.TryGetValue(string.Empty, out var fontName)) { var font = (FontAsset)FindAsset(fontName, typeof(FontAsset)); if (font) - style.Font.Font = font; + style.Font = new MultiFontReference([font], size); } - if (tag.Attributes.TryGetValue("size", out var sizeText) && int.TryParse(sizeText, out var size)) - style.Font.Size = size; + context.StyleStack.Push(style); } } @@ -108,7 +108,7 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - style.Font = style.Font.GetBold(); + // style.Font = style.Font.GetBold(); context.StyleStack.Push(style); } } @@ -122,7 +122,7 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - style.Font = style.Font.GetItalic(); + // style.Font = style.Font.GetItalic(); context.StyleStack.Push(style); } } @@ -136,9 +136,9 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - style.Font = new FontReference(style.Font); - TryParseNumberTag(ref tag, string.Empty, style.Font.Size, out var size); - style.Font.Size = (int)size; + style.Font = new MultiFontReference(style.Font); + TryParseNumberTag(ref tag, string.Empty, style.Font.First().Size, out var size); + style.Font = new MultiFontReference(style.Font, (int)size); context.StyleStack.Push(style); } } @@ -173,9 +173,9 @@ namespace FlaxEngine.GUI imageBlock.Style.BackgroundBrush = image; // Setup size - var font = imageBlock.Style.Font.GetFont(); + var font = imageBlock.Style.Font.GetMultiFont(); if (font) - imageBlock.Bounds.Size = new Float2(font.Height); + imageBlock.Bounds.Size = new Float2(font.MaxHeight); imageBlock.Bounds.Size.X *= image.Size.X / image.Size.Y; // Keep original aspect ratio bool hasWidth = TryParseNumberTag(ref tag, "width", imageBlock.Bounds.Width, out var width); imageBlock.Bounds.Width = width; @@ -213,18 +213,18 @@ namespace FlaxEngine.GUI style.Alignment &= ~TextBlockStyle.Alignments.VerticalMask; switch (valign) { - case "top": - style.Alignment = TextBlockStyle.Alignments.Top; - break; - case "bottom": - style.Alignment = TextBlockStyle.Alignments.Bottom; - break; - case "middle": - style.Alignment = TextBlockStyle.Alignments.Middle; - break; - case "baseline": - style.Alignment = TextBlockStyle.Alignments.Baseline; - break; + case "top": + style.Alignment = TextBlockStyle.Alignments.Top; + break; + case "bottom": + style.Alignment = TextBlockStyle.Alignments.Bottom; + break; + case "middle": + style.Alignment = TextBlockStyle.Alignments.Middle; + break; + case "baseline": + style.Alignment = TextBlockStyle.Alignments.Baseline; + break; } } context.StyleStack.Push(style); @@ -245,15 +245,15 @@ namespace FlaxEngine.GUI style.Alignment &= ~TextBlockStyle.Alignments.VerticalMask; switch (valign) { - case "left": - style.Alignment = TextBlockStyle.Alignments.Left; - break; - case "right": - style.Alignment = TextBlockStyle.Alignments.Right; - break; - case "center": - style.Alignment = TextBlockStyle.Alignments.Center; - break; + case "left": + style.Alignment = TextBlockStyle.Alignments.Left; + break; + case "right": + style.Alignment = TextBlockStyle.Alignments.Right; + break; + case "center": + style.Alignment = TextBlockStyle.Alignments.Center; + break; } } context.StyleStack.Push(style); @@ -280,7 +280,7 @@ namespace FlaxEngine.GUI foreach (var id in ids) { var path = Content.GetEditorAssetPath(id); - if (!string.IsNullOrEmpty(path) && + if (!string.IsNullOrEmpty(path) && string.Equals(name, System.IO.Path.GetFileNameWithoutExtension(path), System.StringComparison.OrdinalIgnoreCase)) { return Content.LoadAsync(id, type); diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.cs b/Source/Engine/UI/GUI/Common/RichTextBox.cs index e6a82c986..78da9621c 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.cs @@ -46,7 +46,7 @@ namespace FlaxEngine.GUI var style = Style.Current; _textStyle = new TextBlockStyle { - Font = new FontReference(style.FontMedium.First()), + Font = new MultiFontReference(style.FontMedium), Color = style.Foreground, BackgroundSelectedBrush = new SolidColorBrush(style.BackgroundSelected), }; diff --git a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs index 438a7e3d8..6247cb885 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs @@ -123,10 +123,10 @@ namespace FlaxEngine.GUI if (index <= 0) { ref TextBlock textBlock = ref textBlocks[0]; - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (font) { - height = font.Height / DpiScale; + height = font.MaxHeight / DpiScale; return textBlock.Bounds.UpperLeft; } } @@ -135,10 +135,10 @@ namespace FlaxEngine.GUI if (index >= _text.Length) { ref TextBlock textBlock = ref textBlocks[count - 1]; - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (font) { - height = font.Height / DpiScale; + height = font.MaxHeight / DpiScale; return textBlock.Bounds.UpperRight; } } @@ -150,10 +150,10 @@ namespace FlaxEngine.GUI if (textBlock.Range.Contains(index)) { - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font) break; - height = font.Height / DpiScale; + height = font.MaxHeight / DpiScale; return textBlock.Bounds.Location + font.GetCharPosition(_text, ref textBlock.Range, index - textBlock.Range.StartIndex); } } @@ -165,10 +165,10 @@ namespace FlaxEngine.GUI if (index >= textBlock.Range.EndIndex) { - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font) break; - height = font.Height / DpiScale; + height = font.MaxHeight / DpiScale; return textBlock.Bounds.UpperRight; } } @@ -193,7 +193,7 @@ namespace FlaxEngine.GUI if (containsY && (containsX || (i + 1 < count && textBlocks[i + 1].Bounds.Location.Y > textBlock.Bounds.Location.Y + 1.0f))) { - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font && textBlock.Range.Length > 0) break; return font.HitTestText(_text, ref textBlock.Range, location - textBlock.Bounds.Location) + textBlock.Range.StartIndex; @@ -281,7 +281,7 @@ namespace FlaxEngine.GUI } // Pick font - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font) continue; @@ -290,7 +290,7 @@ namespace FlaxEngine.GUI { var leftEdge = selection.StartIndex <= textBlock.Range.StartIndex ? textBlock.Bounds.UpperLeft : font.GetCharPosition(_text, selection.StartIndex); var rightEdge = selection.EndIndex >= textBlock.Range.EndIndex ? textBlock.Bounds.UpperRight : font.GetCharPosition(_text, selection.EndIndex); - float height = font.Height / DpiScale; + float height = font.MaxHeight / DpiScale; float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); alpha *= alpha; Color selectionColor = Color.White * alpha; @@ -305,7 +305,7 @@ namespace FlaxEngine.GUI ref TextBlock textBlock = ref textBlocks[i]; // Pick font - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font) continue; @@ -332,7 +332,7 @@ namespace FlaxEngine.GUI ref TextBlock textBlock = ref textBlocks[i]; // Pick font - var font = textBlock.Style.Font.GetFont(); + var font = textBlock.Style.Font.GetMultiFont(); if (!font) continue; @@ -340,7 +340,7 @@ namespace FlaxEngine.GUI if (textBlock.Style.UnderlineBrush != null) { var underLineHeight = 2.0f; - var height = font.Height / DpiScale; + var height = font.MaxHeight / DpiScale; var underlineRect = new Rectangle(textBlock.Bounds.Location.X, textBlock.Bounds.Location.Y + height - underLineHeight * 0.5f, textBlock.Bounds.Width, underLineHeight); textBlock.Style.UnderlineBrush.Draw(underlineRect, textBlock.Style.Color); } diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index 967617649..d9c7c1a80 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -40,7 +40,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font. /// [EditorDisplay("Text Style"), EditorOrder(2024)] - public FontReference Font { get; set; } + public MultiFontReference Font { get; set; } /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. @@ -90,7 +90,7 @@ namespace FlaxEngine.GUI _layout.Bounds = new Rectangle(DefaultMargin, 1, Width - 2 * DefaultMargin, Height - 2); var style = Style.Current; - Font = new FontReference(style.FontMedium.First()); + Font = new MultiFontReference(style.FontMedium); TextColor = style.Foreground; WatermarkTextColor = style.ForegroundDisabled; SelectionColor = style.BackgroundSelected; @@ -99,7 +99,7 @@ namespace FlaxEngine.GUI /// public override Float2 GetTextSize() { - var font = Font.GetFont(); + var font = Font.GetMultiFont(); if (font == null) { return Float2.Zero; @@ -111,21 +111,21 @@ namespace FlaxEngine.GUI /// public override Float2 GetCharPosition(int index, out float height) { - var font = Font.GetFont(); + var font = Font.GetMultiFont(); if (font == null) { height = Height; return Float2.Zero; } - height = font.Height / DpiScale; + height = font.MaxHeight / DpiScale; return font.GetCharPosition(_text, index, ref _layout); } /// public override int HitTestText(Float2 location) { - var font = Font.GetFont(); + var font = Font.GetMultiFont(); if (font == null) { return 0; @@ -148,7 +148,7 @@ namespace FlaxEngine.GUI // Cache data var rect = new Rectangle(Float2.Zero, Size); bool enabled = EnabledInHierarchy; - var font = Font.GetFont(); + var font = Font.GetMultiFont(); if (!font) return; @@ -172,7 +172,7 @@ namespace FlaxEngine.GUI { var leftEdge = font.GetCharPosition(_text, SelectionLeft, ref _layout); var rightEdge = font.GetCharPosition(_text, SelectionRight, ref _layout); - float fontHeight = font.Height / DpiScale; + float fontHeight = font.MaxHeight / DpiScale; // Draw selection background float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index 33c2e1605..06256c9f8 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -130,7 +130,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font used to render panel header text. /// [EditorDisplay("Header Text Style"), EditorOrder(2020), ExpandGroups] - public FontReference HeaderTextFont { get; set; } + public MultiFontReference HeaderTextFont { get; set; } /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. @@ -238,7 +238,7 @@ namespace FlaxEngine.GUI var style = Style.Current; HeaderColor = style.BackgroundNormal; HeaderColorMouseOver = style.BackgroundHighlighted; - HeaderTextFont = new FontReference(style.FontMedium.First()); + HeaderTextFont = new MultiFontReference(style.FontMedium); HeaderTextColor = style.Foreground; ArrowImageOpened = new SpriteBrush(style.ArrowDown); ArrowImageClosed = new SpriteBrush(style.ArrowRight); @@ -375,7 +375,7 @@ namespace FlaxEngine.GUI textColor *= 0.6f; } - Render2D.DrawText(HeaderTextFont.GetFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(HeaderTextFont.GetMultiFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); if (!_isClosed && EnableContainmentLines) { diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index d3375a670..a8b87fb3a 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -14,68 +14,60 @@ namespace FlaxEngine.GUI /// public static Style Current { get; set; } - public Font FontCJK - { - get => _fontCJK?.GetFont(); - set => _fontCJK = new FontReference(value); - } - - private FontReference _fontCJK; - [Serialize] - private FontReference _fontTitle; + private MultiFontReference _fontTitle; /// /// The font title. /// [NoSerialize] [EditorOrder(10)] - public Font FontTitle + public MultiFont FontTitle { - get => _fontTitle?.GetFont(); - set => _fontTitle = new FontReference(value); + get => _fontTitle?.GetMultiFont(); + set => _fontTitle = new MultiFontReference(value); } [Serialize] - private FontReference _fontLarge; + private MultiFontReference _fontLarge; /// /// The font large. /// [NoSerialize] [EditorOrder(20)] - public Font FontLarge + public MultiFont FontLarge { - get => _fontLarge?.GetFont(); - set => _fontLarge = new FontReference(value); + get => _fontLarge?.GetMultiFont(); + set => _fontLarge = new MultiFontReference(value); } [Serialize] - private FontReference[] _fontMedium; + private MultiFontReference _fontMedium; /// /// The font medium. /// [NoSerialize] [EditorOrder(30)] - public Font[] FontMedium + public MultiFont FontMedium { - get => _fontMedium?.Select((x)=>x.GetFont()).ToArray(); - set => _fontMedium = value.Select((x)=>new FontReference(x)).ToArray(); + get => _fontMedium?.GetMultiFont(); + set => _fontMedium = new MultiFontReference(value); } [Serialize] - private FontReference[] _fontSmall; + private MultiFontReference _fontSmall; /// /// The font small. /// [NoSerialize] [EditorOrder(40)] - public Font[] FontSmall + public MultiFont FontSmall { - get => _fontSmall?.Select((x) => x.GetFont()).ToArray(); - set => _fontSmall = value.Select((x) => new FontReference(x)).ToArray(); + get => _fontSmall?.GetMultiFont(); + set => _fontSmall = new MultiFontReference(value); } /// diff --git a/Source/Engine/UI/GUI/TextBlockStyle.cs b/Source/Engine/UI/GUI/TextBlockStyle.cs index c4c87715f..08f74e7c8 100644 --- a/Source/Engine/UI/GUI/TextBlockStyle.cs +++ b/Source/Engine/UI/GUI/TextBlockStyle.cs @@ -64,7 +64,7 @@ namespace FlaxEngine.GUI /// The text font. /// [EditorOrder(0)] - public FontReference Font; + public MultiFontReference Font; /// /// The custom material for the text rendering (must be GUI domain). diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index e17b754c4..7a1026e55 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -244,6 +244,7 @@ namespace FlaxEngine.GUI TextAlignment.Center, TextWrapping.WrapWords ); + } /// @@ -256,14 +257,14 @@ namespace FlaxEngine.GUI // Calculate size of the tooltip var size = Float2.Zero; - if (style != null && style.FontMedium.First() && !string.IsNullOrEmpty(_currentText)) + if (style != null && style.FontMedium && !string.IsNullOrEmpty(_currentText)) { var layout = TextLayoutOptions.Default; layout.Bounds = new Rectangle(0, 0, MaxWidth, 10000000); layout.HorizontalAlignment = TextAlignment.Center; layout.VerticalAlignment = TextAlignment.Center; layout.TextWrapping = TextWrapping.WrapWords; - var items = style.FontMedium.First().ProcessText(_currentText, ref layout); + var items = style.FontMedium.ProcessText(_currentText, ref layout); for (int i = 0; i < items.Length; i++) { ref var item = ref items[i]; From 3365fb5afc6f7600acaa5e730a257bb378f2776f Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:59:43 +0800 Subject: [PATCH 04/16] Fix console rendering bug --- Source/Editor/Options/InterfaceOptions.cs | 11 ++- Source/Editor/Windows/OutputLogWindow.cs | 74 +++++++++---------- .../UI/GUI/Common/RichTextBox.Parsing.cs | 33 ++++----- .../Engine/UI/GUI/Common/RichTextBox.Tags.cs | 27 +++---- Source/Engine/UI/GUI/Common/RichTextBox.cs | 2 +- .../Engine/UI/GUI/Common/RichTextBoxBase.cs | 28 +++---- Source/Engine/UI/GUI/TextBlockStyle.cs | 2 +- 7 files changed, 82 insertions(+), 95 deletions(-) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index e43751fd1..91eebe4eb 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -166,13 +166,13 @@ namespace FlaxEditor.Options /// Gets or sets the output log text font. /// [EditorDisplay("Output Log", "Text Font"), EditorOrder(320), Tooltip("The output log text font.")] - public MultiFontReference OutputLogTextFont + public FontReference OutputLogTextFont { get => _outputLogFont; set { - if (value == null || !value.Verify()) - _outputLogFont = new MultiFontReference(ConsoleFonts, 10); + if (value == null || !value.Font) + _outputLogFont = new FontReference(ConsoleFont, 10); else _outputLogFont = value; } @@ -238,14 +238,13 @@ namespace FlaxEditor.Options [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont), FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CjkFont)]; - private static FontAsset[] ConsoleFonts => [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont), - FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CjkFont)]; + private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); private MultiFontReference _titleFont = new MultiFontReference(DefaultFonts, 18); private MultiFontReference _largeFont = new MultiFontReference(DefaultFonts, 14); private MultiFontReference _mediumFont = new MultiFontReference(DefaultFonts, 9); private MultiFontReference _smallFont = new MultiFontReference(DefaultFonts, 9); - private MultiFontReference _outputLogFont = new MultiFontReference(ConsoleFonts, 10); + private FontReference _outputLogFont = new FontReference(ConsoleFont, 10); /// diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index ac4fea5ba..f168d8d45 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -470,9 +470,9 @@ namespace FlaxEditor.Windows var wasEmpty = _output.TextLength == 0; // Cache fonts - _output.DefaultStyle.Font.GetMultiFont(); - _output.WarningStyle.Font.GetMultiFont(); - _output.ErrorStyle.Font.GetMultiFont(); + _output.DefaultStyle.Font.GetFont(); + _output.WarningStyle.Font.GetFont(); + _output.ErrorStyle.Font.GetFont(); // Generate the output log Span entries = CollectionsMarshal.AsSpan(_entries); @@ -536,7 +536,7 @@ namespace FlaxEditor.Windows } var prevBlockBottom = _textBlocks.Count == 0 ? 0.0f : _textBlocks[_textBlocks.Count - 1].Bounds.Bottom; var entryText = _textBuffer.ToString(startIndex, endIndex - startIndex); - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font) continue; var style = textBlock.Style; @@ -544,52 +544,46 @@ namespace FlaxEditor.Windows for (int j = 0; j < lines.Length; j++) { ref var line = ref lines[j]; - for (int k = 0; k < line.Blocks.Length; k++) + textBlock.Range.StartIndex = startIndex + line.FirstCharIndex; + textBlock.Range.EndIndex = startIndex + line.LastCharIndex + 1; + textBlock.Bounds = new Rectangle(new Float2(0.0f, prevBlockBottom), line.Size); + + if (textBlock.Range.Length > 0) { - ref var block = ref line.Blocks[k]; - - textBlock.Range.StartIndex = startIndex + block.FirstCharIndex; - textBlock.Range.EndIndex = startIndex + block.LastCharIndex + 1; - textBlock.Bounds = new Rectangle(new Float2(block.Location.X, prevBlockBottom), block.Size); - - if (textBlock.Range.Length > 0) + // Parse compilation error/warning + var regexStart = line.FirstCharIndex; + if (j == 0) + regexStart += prefixLength; + var regexLength = line.LastCharIndex + 1 - regexStart; + if (regexLength > 0) { - // Parse compilation error/warning - var regexStart = block.FirstCharIndex; - if (j == 0) - regexStart += prefixLength; - var regexLength = block.LastCharIndex + 1 - regexStart; - if (regexLength > 0) + var match = _compileRegex.Match(entryText, regexStart, regexLength); + if (match.Success) { - var match = _compileRegex.Match(entryText, regexStart, regexLength); - if (match.Success) + switch (match.Groups["level"].Value) { - switch (match.Groups["level"].Value) - { - case "error": - textBlock.Style = _output.ErrorStyle; - break; - case "warning": - textBlock.Style = _output.WarningStyle; - break; - } - textBlock.Tag = new TextBlockTag - { - Type = TextBlockTag.Types.CodeLocation, - Url = match.Groups["path"].Value, - Line = int.Parse(match.Groups["line"].Value), - }; + case "error": + textBlock.Style = _output.ErrorStyle; + break; + case "warning": + textBlock.Style = _output.WarningStyle; + break; } - // TODO: parsing hyperlinks with link - // TODO: parsing file paths with link + textBlock.Tag = new TextBlockTag + { + Type = TextBlockTag.Types.CodeLocation, + Url = match.Groups["path"].Value, + Line = int.Parse(match.Groups["line"].Value), + }; } + // TODO: parsing hyperlinks with link + // TODO: parsing file paths with link } - - _textBlocks.Add(textBlock); - textBlock.Style = style; } prevBlockBottom += line.Size.Y; + _textBlocks.Add(textBlock); + textBlock.Style = style; } } diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs index 9523ad30a..ab01df12b 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs @@ -186,7 +186,7 @@ namespace FlaxEngine.GUI }; // Process text into text blocks (handle newlines etc.) - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font) return; var lines = font.ProcessText(_text, ref textBlock.Range); @@ -195,27 +195,20 @@ namespace FlaxEngine.GUI for (int i = 0; i < lines.Length; i++) { ref var line = ref lines[i]; - + textBlock.Range = new TextRange + { + StartIndex = start + line.FirstCharIndex, + EndIndex = start + line.LastCharIndex, + }; if (i != 0) { context.Caret.X = 0; OnLineAdded(ref context, textBlock.Range.StartIndex - 1); } - for (int k = 0; k < line.Blocks.Length; k++) - { - ref var block = ref line.Blocks[k]; + textBlock.Bounds = new Rectangle(context.Caret, line.Size); + textBlock.Bounds.X += line.Location.X; - textBlock.Range = new TextRange - { - StartIndex = start + block.FirstCharIndex, - EndIndex = start + block.LastCharIndex, - }; - - textBlock.Bounds = new Rectangle(context.Caret, block.Size); - textBlock.Bounds.X += block.Location.X; - - context.AddTextBlock(ref textBlock); - } + context.AddTextBlock(ref textBlock); } // Update the caret location @@ -244,9 +237,9 @@ namespace FlaxEngine.GUI var ascender = textBlock.Ascender; //if (ascender <= 0) { - var textBlockFont = textBlock.Style.Font.GetMultiFont(); + var textBlockFont = textBlock.Style.Font.GetFont(); if (textBlockFont) - ascender = textBlockFont.MaxAscender; + ascender = textBlockFont.Ascender; } lineAscender = Mathf.Max(lineAscender, ascender); lineSize = Float2.Max(lineSize, textBlockSize); @@ -267,9 +260,9 @@ namespace FlaxEngine.GUI var ascender = textBlock.Ascender; if (ascender <= 0) { - var textBlockFont = textBlock.Style.Font.GetMultiFont(); + var textBlockFont = textBlock.Style.Font.GetFont(); if (textBlockFont) - ascender = textBlockFont.MaxAscender; + ascender = textBlockFont.Ascender; } vOffset = lineAscender - ascender; textBlock.Bounds.Location.Y += vOffset; diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs index 08637918d..d3c2feefe 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs @@ -11,9 +11,9 @@ namespace FlaxEngine.GUI { context.Caret.X = 0; var style = context.StyleStack.Peek(); - var font = style.Font.GetMultiFont(); + var font = style.Font.GetFont(); if (font) - context.Caret.Y += font.MaxHeight; + context.Caret.Y += font.Height; } private static void ProcessColor(ref ParsingContext context, ref HtmlTag tag) @@ -87,14 +87,15 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - style.Font = new MultiFontReference(style.Font); - if (tag.Attributes.TryGetValue("size", out var sizeText) && int.TryParse(sizeText, out var size) && tag.Attributes.TryGetValue(string.Empty, out var fontName)) + style.Font = new FontReference(style.Font); + if (tag.Attributes.TryGetValue(string.Empty, out var fontName)) { var font = (FontAsset)FindAsset(fontName, typeof(FontAsset)); if (font) - style.Font = new MultiFontReference([font], size); + style.Font.Font = font; } - + if (tag.Attributes.TryGetValue("size", out var sizeText) && int.TryParse(sizeText, out var size)) + style.Font.Size = size; context.StyleStack.Push(style); } } @@ -108,7 +109,7 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - // style.Font = style.Font.GetBold(); + style.Font = style.Font.GetBold(); context.StyleStack.Push(style); } } @@ -122,7 +123,7 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - // style.Font = style.Font.GetItalic(); + style.Font = style.Font.GetItalic(); context.StyleStack.Push(style); } } @@ -136,9 +137,9 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - style.Font = new MultiFontReference(style.Font); - TryParseNumberTag(ref tag, string.Empty, style.Font.First().Size, out var size); - style.Font = new MultiFontReference(style.Font, (int)size); + style.Font = new FontReference(style.Font); + TryParseNumberTag(ref tag, string.Empty, style.Font.Size, out var size); + style.Font.Size = (int)size; context.StyleStack.Push(style); } } @@ -173,9 +174,9 @@ namespace FlaxEngine.GUI imageBlock.Style.BackgroundBrush = image; // Setup size - var font = imageBlock.Style.Font.GetMultiFont(); + var font = imageBlock.Style.Font.GetFont(); if (font) - imageBlock.Bounds.Size = new Float2(font.MaxHeight); + imageBlock.Bounds.Size = new Float2(font.Height); imageBlock.Bounds.Size.X *= image.Size.X / image.Size.Y; // Keep original aspect ratio bool hasWidth = TryParseNumberTag(ref tag, "width", imageBlock.Bounds.Width, out var width); imageBlock.Bounds.Width = width; diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.cs b/Source/Engine/UI/GUI/Common/RichTextBox.cs index 78da9621c..f8ac7d353 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.cs @@ -46,7 +46,7 @@ namespace FlaxEngine.GUI var style = Style.Current; _textStyle = new TextBlockStyle { - Font = new MultiFontReference(style.FontMedium), + Font = new FontReference(style.FontMedium.Fonts.First()), Color = style.Foreground, BackgroundSelectedBrush = new SolidColorBrush(style.BackgroundSelected), }; diff --git a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs index 6247cb885..438a7e3d8 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs @@ -123,10 +123,10 @@ namespace FlaxEngine.GUI if (index <= 0) { ref TextBlock textBlock = ref textBlocks[0]; - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (font) { - height = font.MaxHeight / DpiScale; + height = font.Height / DpiScale; return textBlock.Bounds.UpperLeft; } } @@ -135,10 +135,10 @@ namespace FlaxEngine.GUI if (index >= _text.Length) { ref TextBlock textBlock = ref textBlocks[count - 1]; - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (font) { - height = font.MaxHeight / DpiScale; + height = font.Height / DpiScale; return textBlock.Bounds.UpperRight; } } @@ -150,10 +150,10 @@ namespace FlaxEngine.GUI if (textBlock.Range.Contains(index)) { - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font) break; - height = font.MaxHeight / DpiScale; + height = font.Height / DpiScale; return textBlock.Bounds.Location + font.GetCharPosition(_text, ref textBlock.Range, index - textBlock.Range.StartIndex); } } @@ -165,10 +165,10 @@ namespace FlaxEngine.GUI if (index >= textBlock.Range.EndIndex) { - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font) break; - height = font.MaxHeight / DpiScale; + height = font.Height / DpiScale; return textBlock.Bounds.UpperRight; } } @@ -193,7 +193,7 @@ namespace FlaxEngine.GUI if (containsY && (containsX || (i + 1 < count && textBlocks[i + 1].Bounds.Location.Y > textBlock.Bounds.Location.Y + 1.0f))) { - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font && textBlock.Range.Length > 0) break; return font.HitTestText(_text, ref textBlock.Range, location - textBlock.Bounds.Location) + textBlock.Range.StartIndex; @@ -281,7 +281,7 @@ namespace FlaxEngine.GUI } // Pick font - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font) continue; @@ -290,7 +290,7 @@ namespace FlaxEngine.GUI { var leftEdge = selection.StartIndex <= textBlock.Range.StartIndex ? textBlock.Bounds.UpperLeft : font.GetCharPosition(_text, selection.StartIndex); var rightEdge = selection.EndIndex >= textBlock.Range.EndIndex ? textBlock.Bounds.UpperRight : font.GetCharPosition(_text, selection.EndIndex); - float height = font.MaxHeight / DpiScale; + float height = font.Height / DpiScale; float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); alpha *= alpha; Color selectionColor = Color.White * alpha; @@ -305,7 +305,7 @@ namespace FlaxEngine.GUI ref TextBlock textBlock = ref textBlocks[i]; // Pick font - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font) continue; @@ -332,7 +332,7 @@ namespace FlaxEngine.GUI ref TextBlock textBlock = ref textBlocks[i]; // Pick font - var font = textBlock.Style.Font.GetMultiFont(); + var font = textBlock.Style.Font.GetFont(); if (!font) continue; @@ -340,7 +340,7 @@ namespace FlaxEngine.GUI if (textBlock.Style.UnderlineBrush != null) { var underLineHeight = 2.0f; - var height = font.MaxHeight / DpiScale; + var height = font.Height / DpiScale; var underlineRect = new Rectangle(textBlock.Bounds.Location.X, textBlock.Bounds.Location.Y + height - underLineHeight * 0.5f, textBlock.Bounds.Width, underLineHeight); textBlock.Style.UnderlineBrush.Draw(underlineRect, textBlock.Style.Color); } diff --git a/Source/Engine/UI/GUI/TextBlockStyle.cs b/Source/Engine/UI/GUI/TextBlockStyle.cs index 08f74e7c8..c4c87715f 100644 --- a/Source/Engine/UI/GUI/TextBlockStyle.cs +++ b/Source/Engine/UI/GUI/TextBlockStyle.cs @@ -64,7 +64,7 @@ namespace FlaxEngine.GUI /// The text font. /// [EditorOrder(0)] - public MultiFontReference Font; + public FontReference Font; /// /// The custom material for the text rendering (must be GUI domain). From d3840bb1f36fafb2ca6d5154d9922b3c16b500fb Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Thu, 30 Nov 2023 16:25:32 +0800 Subject: [PATCH 05/16] Refractor C++ code structure --- Source/Engine/Render2D/FallbackFonts.cpp | 11 + Source/Engine/Render2D/FallbackFonts.h | 94 +++++ Source/Engine/Render2D/Font.cpp | 441 ++++++++++++++++++++++- Source/Engine/Render2D/Font.h | 276 +++++++++++++- Source/Engine/Render2D/FontAsset.cpp | 4 + Source/Engine/Render2D/FontAsset.h | 7 + Source/Engine/Render2D/MultiFont.cpp | 431 ---------------------- Source/Engine/Render2D/MultiFont.h | 353 ------------------ Source/Engine/Render2D/Render2D.cpp | 74 ++-- Source/Engine/Render2D/Render2D.h | 10 +- 10 files changed, 864 insertions(+), 837 deletions(-) create mode 100644 Source/Engine/Render2D/FallbackFonts.cpp create mode 100644 Source/Engine/Render2D/FallbackFonts.h delete mode 100644 Source/Engine/Render2D/MultiFont.cpp delete mode 100644 Source/Engine/Render2D/MultiFont.h diff --git a/Source/Engine/Render2D/FallbackFonts.cpp b/Source/Engine/Render2D/FallbackFonts.cpp new file mode 100644 index 000000000..b79a0db4e --- /dev/null +++ b/Source/Engine/Render2D/FallbackFonts.cpp @@ -0,0 +1,11 @@ +#include "FallbackFonts.h" +#include "FontManager.h" +#include "Engine/Core/Math/Math.h" + +FallbackFonts::FallbackFonts(const Array& fonts) + : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)), + _fontAssets(fonts) +{ + +} + diff --git a/Source/Engine/Render2D/FallbackFonts.h b/Source/Engine/Render2D/FallbackFonts.h new file mode 100644 index 000000000..4b97e1c11 --- /dev/null +++ b/Source/Engine/Render2D/FallbackFonts.h @@ -0,0 +1,94 @@ +#pragma once + +#include "Engine/Core/Collections/Array.h" +#include "Engine/Core/Collections/Dictionary.h" +#include "Font.h" +#include "FontAsset.h" + +struct TextRange; +class Font; +class FontAsset; + +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; + + Dictionary*> _cache; + +public: + FallbackFonts(const Array& fonts); + + API_FUNCTION() FORCE_INLINE static FallbackFonts* Create(const Array& fonts) { + return New(fonts); + } + + API_PROPERTY() FORCE_INLINE Array& GetFonts() { + return _fontAssets; + } + + API_PROPERTY() FORCE_INLINE void SetFonts(const Array& val) { + _fontAssets = val; + } + + /// + /// Combine the primary fonts with the fallback fonts to get a font list + /// + API_PROPERTY() FORCE_INLINE Array& GetFontList(float size) { + Array* result; + if (_cache.TryGet(size, result)) { + return *result; + } + + result = New>(_fontAssets.Count()); + auto& arr = *result; + for (int32 i = 0; i < _fontAssets.Count(); i++) + { + arr.Add(_fontAssets[i]->CreateFont(size)); + } + + _cache[size] = result; + return *result; + } + + + /// + /// Gets the index of the fallback font that should be used to render the char + /// + /// The char. + /// -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)) { + return -1; + } + + int32 fontIndex = 0; + while (fontIndex < _fontAssets.Count() && _fontAssets[fontIndex] && !_fontAssets[fontIndex]->ContainsChar(c)) + { + fontIndex++; + } + + if (fontIndex < _fontAssets.Count()) { + return fontIndex; + + } + + return missing; + } + + API_FUNCTION() FORCE_INLINE bool Verify() { + for (int32 i = 0; i < _fontAssets.Count(); i++) + { + if (!_fontAssets[i]) { + return false; + } + } + + return true; + } +}; diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index b33b10899..4cf1f73a2 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -6,7 +6,7 @@ #include "Engine/Core/Log.h" #include "Engine/Threading/Threading.h" #include "IncludeFreeType.h" -#include "MultiFont.h" +#include "FallbackFonts.h" Font::Font(FontAsset* parentAsset, float size) : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)) @@ -293,6 +293,261 @@ void Font::ProcessText(const StringView& text, Array& outputLines } } +void Font::ProcessText(FallbackFonts* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) +{ + const Array& fallbackFonts = fallbacks->GetFontList(GetSize()); + float cursorX = 0; + int32 kerning; + BlockedTextLineCache tmpLine; + FontBlockCache tmpBlock; + FontCharacterEntry entry; + FontCharacterEntry previous; + int32 textLength = text.Length(); + float scale = layout.Scale / FontManager::FontScale; + float boundsWidth = layout.Bounds.GetWidth(); + float baseLinesDistanceScale = layout.BaseLinesGapScale * scale; + + tmpBlock.Location = Float2::Zero; + tmpBlock.Size = Float2::Zero; + tmpBlock.FirstCharIndex = 0; + tmpBlock.LastCharIndex = -1; + + tmpLine.Location = Float2::Zero; + tmpLine.Size = Float2::Zero; + tmpLine.Blocks = Array(); + + if (textLength == 0) { + return; + } + + int32 lastWrapCharIndex = INVALID_INDEX; + float lastWrapCharX = 0; + bool lastMoveLine = false; + // The index of the font used by the current block + int32 currentFontIndex = fallbacks->GetCharFallbackIndex(text[0], this); + // The maximum font height of the current line + float maxHeight = 0; + float maxAscender = 0; + float lastCursorX = 0; + + auto getFont = [&](int32 index)->Font* { + return index >= 0 ? fallbackFonts[index] : this; + }; + + // Process each character to split text into single blocks + for (int32 currentIndex = 0; currentIndex < textLength;) + { + bool moveLine = false; + bool moveBlock = false; + float xAdvance = 0; + int32 nextCharIndex = currentIndex + 1; + + // Submit line and block if text ends + if (nextCharIndex == textLength) { + moveLine = moveBlock = true; + } + + // Cache current character + const Char currentChar = text[currentIndex]; + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); + + // Check if character can wrap words + const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar) || (currentChar >= 0x3040 && currentChar <= 0x9FFF); + if (isWrapChar && currentIndex != 0) + { + lastWrapCharIndex = currentIndex; + lastWrapCharX = cursorX; + } + + int32 nextFontIndex = currentFontIndex; + // Check if it's a newline character + if (currentChar == '\n') + { + // Break line + moveLine = moveBlock = true; + tmpBlock.LastCharIndex++; + } + else + { + // Get character entry + if (nextCharIndex < textLength) { + nextFontIndex = fallbacks->GetCharFallbackIndex(text[nextCharIndex], this, currentFontIndex); + } + + // Get character entry + getFont(currentFontIndex)->GetCharacter(currentChar, entry); + + maxHeight = Math::Max(maxHeight, + static_cast(getFont(currentFontIndex)->GetHeight())); + maxAscender = Math::Max(maxAscender, + static_cast(getFont(currentFontIndex)->GetAscender())); + + // Move block if the font changes or text ends + if (nextFontIndex != currentFontIndex || nextCharIndex == textLength) { + moveBlock = true; + } + + // Get kerning, only when the font hasn't changed + if (!isWhitespace && previous.IsValid && !moveBlock) + { + kerning = getFont(currentFontIndex)->GetKerning(previous.Character, entry.Character); + } + else + { + kerning = 0; + } + previous = entry; + xAdvance = (kerning + entry.AdvanceX) * scale; + + // Check if character fits the line or skip wrapping + if (cursorX + xAdvance <= boundsWidth || layout.TextWrapping == TextWrapping::NoWrap) + { + // Move character + cursorX += xAdvance; + tmpBlock.LastCharIndex++; + } + else if (layout.TextWrapping == TextWrapping::WrapWords) + { + if (lastWrapCharIndex != INVALID_INDEX) + { + // Skip moving twice for the same character + int32 lastLineLastCharIndex = outputLines.HasItems() && outputLines.Last().Blocks.HasItems() ? outputLines.Last().Blocks.Last().LastCharIndex : -10000; + if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2) + { + currentIndex = nextCharIndex; + lastMoveLine = moveLine; + continue; + } + + // Move line + const Char wrapChar = text[lastWrapCharIndex]; + moveLine = true; + moveBlock = tmpBlock.FirstCharIndex < lastWrapCharIndex; + + cursorX = lastWrapCharX; + if (StringUtils::IsWhitespace(wrapChar)) + { + // Skip whitespaces + tmpBlock.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex + 1; + } + else + { + tmpBlock.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex; + } + } + } + else if (layout.TextWrapping == TextWrapping::WrapChars) + { + // Move line + moveLine = true; + moveBlock = tmpBlock.FirstCharIndex < currentChar; + nextCharIndex = currentIndex; + + // Skip moving twice for the same character + if (lastMoveLine) + break; + } + } + + if (moveBlock) { + // Add block + tmpBlock.Size.X = lastCursorX - cursorX; + tmpBlock.Size.Y = baseLinesDistanceScale * getFont(currentFontIndex)->GetHeight(); + tmpBlock.LastCharIndex = Math::Max(tmpBlock.LastCharIndex, tmpBlock.FirstCharIndex); + tmpBlock.FallbackFontIndex = currentFontIndex; + tmpLine.Blocks.Add(tmpBlock); + + // Reset block + tmpBlock.Location.X = cursorX; + tmpBlock.FirstCharIndex = nextCharIndex; + tmpBlock.LastCharIndex = nextCharIndex - 1; + + currentFontIndex = nextFontIndex; + lastCursorX = cursorX; + } + + // Check if move to another line + if (moveLine) + { + // Add line + tmpLine.Size.X = cursorX; + tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; + tmpLine.MaxAscender = maxAscender; + outputLines.Add(tmpLine); + + // Reset line + tmpLine.Blocks.Clear(); + tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; + cursorX = 0; + tmpBlock.Location.X = cursorX; + lastWrapCharIndex = INVALID_INDEX; + lastWrapCharX = 0; + previous.IsValid = false; + + // Reset max font height + maxHeight = 0; + maxAscender = 0; + lastCursorX = 0; + } + + currentIndex = nextCharIndex; + lastMoveLine = moveLine; + } + + // Check if an additional line should be created + if (text[textLength - 1] == '\n') + { + // Add line + tmpLine.Size.X = cursorX; + tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; + outputLines.Add(tmpLine); + + tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; + } + + // Check amount of lines + if (outputLines.IsEmpty()) + return; + + float totalHeight = tmpLine.Location.Y; + + Float2 offset = Float2::Zero; + if (layout.VerticalAlignment == TextAlignment::Center) + { + offset.Y += (layout.Bounds.GetHeight() - totalHeight) * 0.5f; + } + else if (layout.VerticalAlignment == TextAlignment::Far) + { + offset.Y += layout.Bounds.GetHeight() - totalHeight; + } + for (int32 i = 0; i < outputLines.Count(); i++) + { + BlockedTextLineCache& line = outputLines[i]; + Float2 rootPos = line.Location + offset; + + // Fix upper left line corner to match desire text alignment + if (layout.HorizontalAlignment == TextAlignment::Center) + { + rootPos.X += (layout.Bounds.GetWidth() - line.Size.X) * 0.5f; + } + else if (layout.HorizontalAlignment == TextAlignment::Far) + { + rootPos.X += layout.Bounds.GetWidth() - line.Size.X; + } + + line.Location = rootPos; + + // Align all blocks to center in case they have different heights + for (int32 j = 0; j < line.Blocks.Count(); j++) + { + FontBlockCache& block = line.Blocks[j]; + block.Location.Y += (line.MaxAscender - getFont(block.FallbackFontIndex)->GetAscender()) / 2; + } + } +} + Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout) { // Check if there is no need to do anything @@ -314,6 +569,27 @@ Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout return max; } +Float2 Font::MeasureText(FallbackFonts* fallbacks, const StringView& text, const TextLayoutOptions& layout) +{ + // Check if there is no need to do anything + if (text.IsEmpty()) + return Float2::Zero; + + // Process text + Array lines; + ProcessText(fallbacks, text, lines, layout); + + // Calculate bounds + Float2 max = Float2::Zero; + for (int32 i = 0; i < lines.Count(); i++) + { + const BlockedTextLineCache& line = lines[i]; + max = Float2::Max(max, line.Location + line.Size); + } + + return max; +} + int32 Font::HitTestText(const StringView& text, const Float2& location, const TextLayoutOptions& layout) { // Check if there is no need to do anything @@ -388,6 +664,106 @@ int32 Font::HitTestText(const StringView& text, const Float2& location, const Te return smallestIndex; } +int32 Font::HitTestText(FallbackFonts* fallbacks, const StringView& text, const Float2& location, const TextLayoutOptions& layout) +{ + // Check if there is no need to do anything + if (text.Length() <= 0) + return 0; + + // Process text + const Array& fallbackFonts = fallbacks->GetFontList(GetSize()); + Array lines; + ProcessText(fallbacks, text, lines, layout); + ASSERT(lines.HasItems()); + float scale = layout.Scale / FontManager::FontScale; + + // Offset position to match lines origin space + Float2 rootOffset = layout.Bounds.Location + lines.First().Location; + Float2 testPoint = location - rootOffset; + + // Get block which may intersect with the position (it's possible because lines have fixed height) + int32 lineIndex = 0; + while (lineIndex < lines.Count()) + { + if (lines[lineIndex].Location.Y + lines[lineIndex].Size.Y >= location.Y) { + break; + } + + lineIndex++; + } + lineIndex = Math::Clamp(lineIndex, 0, lines.Count() - 1); + const BlockedTextLineCache& line = lines[lineIndex]; + + int32 blockIndex = 0; + while (blockIndex < line.Blocks.Count() - 1) + { + if (line.Location.X + line.Blocks[blockIndex + 1].Location.X >= location.X) { + break; + } + + blockIndex++; + } + const FontBlockCache& block = line.Blocks[blockIndex]; + float x = line.Location.X; + + // Check all characters in the line to find hit point + FontCharacterEntry previous; + FontCharacterEntry entry; + int32 smallestIndex = INVALID_INDEX; + float dst, smallestDst = MAX_float; + + auto getFont = [&](int32 index)->Font* { + return index >= 0 ? fallbackFonts[index] : this; + }; + + for (int32 currentIndex = block.FirstCharIndex; currentIndex <= block.LastCharIndex; currentIndex++) + { + // Cache current character + const Char currentChar = text[currentIndex]; + + getFont(block.FallbackFontIndex)->GetCharacter(currentChar, entry); + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); + + // Apply kerning + if (!isWhitespace && previous.IsValid) + { + x += getFont(block.FallbackFontIndex)->GetKerning(previous.Character, entry.Character); + } + previous = entry; + + // Test + dst = Math::Abs(testPoint.X - x); + if (dst < smallestDst) + { + // Found closer character + smallestIndex = currentIndex; + smallestDst = dst; + } + else if (dst > smallestDst) + { + // Current char is worse so return the best result + return smallestIndex; + } + + // Move + x += entry.AdvanceX * scale; + } + + // Test line end edge + dst = Math::Abs(testPoint.X - x); + if (dst < smallestDst) + { + // Pointer is behind the last character in the line + smallestIndex = block.LastCharIndex; + + // Fix for last line + if (lineIndex == lines.Count() - 1) + smallestIndex++; + } + + return smallestIndex; +} + Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayoutOptions& layout) { // Check if there is no need to do anything @@ -442,9 +818,68 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); } -bool Font::ContainsChar(Char c) const +Float2 Font::GetCharPosition(FallbackFonts* fallbacks, const StringView& text, int32 index, const TextLayoutOptions& layout) { - return FT_Get_Char_Index(GetAsset()->GetFTFace(), c) > 0; + // Check if there is no need to do anything + if (text.IsEmpty()) + return layout.Bounds.Location; + + // Process text + const Array& fallbackFonts = fallbacks->GetFontList(GetSize()); + Array lines; + ProcessText(fallbacks, text, lines, layout); + ASSERT(lines.HasItems()); + float scale = layout.Scale / FontManager::FontScale; + float baseLinesDistance = layout.BaseLinesGapScale * scale; + Float2 rootOffset = layout.Bounds.Location; + + // Find line with that position + FontCharacterEntry previous; + FontCharacterEntry entry; + + auto getFont = [&](int32 index)->Font* { + return index >= 0 ? fallbackFonts[index] : this; + }; + + for (int32 lineIndex = 0; lineIndex < lines.Count(); lineIndex++) + { + const BlockedTextLineCache& line = lines[lineIndex]; + for (int32 blockIndex = 0; blockIndex < line.Blocks.Count(); blockIndex++) + { + const FontBlockCache& block = line.Blocks[blockIndex]; + // Check if desire position is somewhere inside characters in line range + if (Math::IsInRange(index, block.FirstCharIndex, block.LastCharIndex)) + { + float x = line.Location.X + block.Location.X; + float y = line.Location.Y + block.Location.Y; + + // Check all characters in the line + for (int32 currentIndex = block.FirstCharIndex; currentIndex < index; currentIndex++) + { + // Cache current character + const Char currentChar = text[currentIndex]; + getFont(block.FallbackFontIndex)->GetCharacter(currentChar, entry); + const bool isWhitespace = StringUtils::IsWhitespace(currentChar); + + // Apply kerning + if (!isWhitespace && previous.IsValid) + { + x += getFont(block.FallbackFontIndex)->GetKerning(previous.Character, entry.Character); + } + previous = entry; + + // Move + x += entry.AdvanceX * scale; + } + + // Upper left corner of the character + return rootOffset + Float2(x, y); + } + } + } + + // Position after last character in the last line + return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, lines.Last().Location.Y); } void Font::FlushFaceSize() const diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index 453872582..d932888fb 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -10,8 +10,9 @@ #include "TextLayoutOptions.h" class FontAsset; +class FallbackFonts; struct FontTextureAtlasSlot; -struct MultiFontLineCache; +struct BlockedTextLineCache; // The default DPI that engine is using #define DefaultDPI 96 @@ -120,6 +121,73 @@ struct TIsPODType enum { Value = true }; }; +/// +/// The font block info generated during text processing. +/// +API_STRUCT(NoDefault) struct FontBlockCache +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(FontBlockCache); + + /// + /// The root position of the block (upper left corner), relative to line. + /// + API_FIELD() Float2 Location; + + /// + /// The height of the current block + /// + API_FIELD() Float2 Size; + + /// + /// The first character index (from the input text). + /// + API_FIELD() int32 FirstCharIndex; + + /// + /// The last character index (from the input text), inclusive. + /// + API_FIELD() int32 LastCharIndex; + + /// + /// Indicates the fallback font to render this block with, -1 if doesn't require fallback. + /// + API_FIELD() int32 FallbackFontIndex; +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; + +/// +/// Line of font blocks info generated during text processing. +/// +API_STRUCT(NoDefault) struct BlockedTextLineCache +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(BlockedTextLineCache); + + /// + /// The root position of the line (upper left corner). + /// + API_FIELD() Float2 Location; + + /// + /// The line bounds (width and height). + /// + API_FIELD() Float2 Size; + + /// + /// The maximum ascender of the line. + /// + API_FIELD() float MaxAscender; + + /// + /// The index of the font to render with + /// + API_FIELD() Array Blocks; +}; + // Font glyph metrics: // // xmin xmax @@ -388,6 +456,62 @@ public: return ProcessText(textRange.Substring(text), TextLayoutOptions()); } + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The layout properties. + /// The output lines list. + void ProcessText(FallbackFonts* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The layout properties. + /// The output lines list. + API_FUNCTION() Array ProcessText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) + { + Array lines; + ProcessText(fallbacks, text, lines, layout); + return lines; + } + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The input text range (substring range of the input text parameter). + /// The layout properties. + /// The output lines list. + API_FUNCTION() Array ProcessText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + { + Array lines; + ProcessText(fallbacks, textRange.Substring(text), lines, layout); + return lines; + } + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The output lines list. + API_FUNCTION() FORCE_INLINE Array ProcessText(FallbackFonts* fallbacks, const StringView& text) + { + return ProcessText(fallbacks, text, TextLayoutOptions()); + } + + /// + /// Processes text to get cached lines for rendering. + /// + /// The input text. + /// The input text range (substring range of the input text parameter). + /// The output lines list. + API_FUNCTION() FORCE_INLINE Array ProcessText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) + { + return ProcessText(fallbacks, textRange.Substring(text), TextLayoutOptions()); + } + /// /// Measures minimum size of the rectangle that will be needed to draw given text. /// @@ -429,6 +553,56 @@ public: return MeasureText(textRange.Substring(text), TextLayoutOptions()); } + /// + /// Measures minimum size of the rectangle that will be needed to draw given text. + /// + /// The input text to test. + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return MeasureText(fallbacks, textRange.Substring(text), layout); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text + /// . + /// The input text to test. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text) + { + return MeasureText(fallbacks, text, TextLayoutOptions()); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text + /// . + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) + { + return MeasureText(fallbacks, textRange.Substring(text), TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); + /// /// Calculates hit character index at given location. /// @@ -442,15 +616,6 @@ public: return HitTestText(textRange.Substring(text), location, layout); } - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); - /// /// Calculates hit character index at given location. /// @@ -474,6 +639,51 @@ public: return HitTestText(textRange.Substring(text), location, TextLayoutOptions()); } + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return HitTestText(fallbacks, textRange.Substring(text), location, layout); + } + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, const Float2& location) + { + return HitTestText(fallbacks, text, location, TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) + { + return HitTestText(fallbacks, textRange.Substring(text), location, TextLayoutOptions()); + } + /// /// Calculates character position for given text and character index. /// @@ -520,11 +730,49 @@ public: } /// - /// Check if the font contains the glyph of a char + /// Calculates character position for given text and character index. /// - /// 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; + /// The input text to test. + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Calculates character position for given text and character index. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return GetCharPosition(fallbacks, textRange.Substring(text), index, layout); + } + + /// + /// Calculates character position for given text and character index + /// + /// The input text to test. + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, int32 index) + { + return GetCharPosition(fallbacks, text, index, TextLayoutOptions()); + } + + /// + /// Calculates character position for given text and character index + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) + { + return GetCharPosition(fallbacks, textRange.Substring(text), index, TextLayoutOptions()); + } /// /// Flushes the size of the face with the Free Type library backend. diff --git a/Source/Engine/Render2D/FontAsset.cpp b/Source/Engine/Render2D/FontAsset.cpp index a477e5f4d..1e94b19b1 100644 --- a/Source/Engine/Render2D/FontAsset.cpp +++ b/Source/Engine/Render2D/FontAsset.cpp @@ -199,6 +199,10 @@ bool FontAsset::Save(const StringView& path) #endif +bool FontAsset::ContainsChar(Char c) const { + return FT_Get_Char_Index(GetFTFace(), c) > 0; +} + void FontAsset::Invalidate() { ScopeLock lock(Locker); diff --git a/Source/Engine/Render2D/FontAsset.h b/Source/Engine/Render2D/FontAsset.h index 4dea84a5b..a773a8ad6 100644 --- a/Source/Engine/Render2D/FontAsset.h +++ b/Source/Engine/Render2D/FontAsset.h @@ -174,6 +174,13 @@ public: API_FUNCTION() bool Save(const StringView& path = StringView::Empty); #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. + API_FUNCTION() FORCE_INLINE 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/Render2D/MultiFont.cpp b/Source/Engine/Render2D/MultiFont.cpp deleted file mode 100644 index 4a510a9f8..000000000 --- a/Source/Engine/Render2D/MultiFont.cpp +++ /dev/null @@ -1,431 +0,0 @@ -#include "MultiFont.h" -#include "FontManager.h" -#include "Engine/Core/Math/Math.h" - -MultiFont::MultiFont(const Array& fonts) - : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)), - _fonts(fonts) -{ - -} - -void MultiFont::ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) -{ - float cursorX = 0; - int32 kerning; - MultiFontLineCache tmpLine; - MultiFontBlockCache tmpBlock; - FontCharacterEntry entry; - FontCharacterEntry previous; - int32 textLength = text.Length(); - float scale = layout.Scale / FontManager::FontScale; - float boundsWidth = layout.Bounds.GetWidth(); - float baseLinesDistanceScale = layout.BaseLinesGapScale * scale; - - tmpBlock.Location = Float2::Zero; - tmpBlock.Size = Float2::Zero; - tmpBlock.FirstCharIndex = 0; - tmpBlock.LastCharIndex = -1; - - tmpLine.Location = Float2::Zero; - tmpLine.Size = Float2::Zero; - tmpLine.Blocks = Array(); - - if (textLength == 0) { - return; - } - - int32 lastWrapCharIndex = INVALID_INDEX; - float lastWrapCharX = 0; - bool lastMoveLine = false; - // The index of the font used by the current block - int32 currentFontIndex = GetCharFontIndex(text[0], 0); - // The maximum font height of the current line - float maxHeight = 0; - float maxAscender = 0; - float lastCursorX = 0; - - // Process each character to split text into single lines - for (int32 currentIndex = 0; currentIndex < textLength;) - { - bool moveLine = false; - bool moveBlock = false; - float xAdvance = 0; - int32 nextCharIndex = currentIndex + 1; - - // Submit line and block if text ends - if (nextCharIndex == textLength) { - moveLine = moveBlock = true; - } - - // Cache current character - const Char currentChar = text[currentIndex]; - const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - - // Check if character can wrap words - const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar) || (currentChar >= 0x3040 && currentChar <= 0x9FFF); - if (isWrapChar && currentIndex != 0) - { - lastWrapCharIndex = currentIndex; - lastWrapCharX = cursorX; - } - - int32 nextFontIndex = currentFontIndex; - // Check if it's a newline character - if (currentChar == '\n') - { - // Break line - moveLine = moveBlock = true; - tmpBlock.LastCharIndex++; - } - else - { - // Get character entry - if (nextCharIndex < textLength) { - nextFontIndex = GetCharFontIndex(text[nextCharIndex], currentFontIndex); - } - - // Get character entry - _fonts[currentFontIndex]->GetCharacter(currentChar, entry); - maxHeight = Math::Max(maxHeight, static_cast(_fonts[currentFontIndex]->GetHeight())); - maxAscender = Math::Max(maxAscender, static_cast(_fonts[currentFontIndex]->GetAscender())); - - // Move block if the font changes or text ends - if (nextFontIndex != currentFontIndex || nextCharIndex == textLength) { - moveBlock = true; - } - - // Get kerning, only when the font hasn't changed - if (!isWhitespace && previous.IsValid && !moveBlock) - { - kerning = _fonts[currentFontIndex]->GetKerning(previous.Character, entry.Character); - } - else - { - kerning = 0; - } - previous = entry; - xAdvance = (kerning + entry.AdvanceX) * scale; - - // Check if character fits the line or skip wrapping - if (cursorX + xAdvance <= boundsWidth || layout.TextWrapping == TextWrapping::NoWrap) - { - // Move character - cursorX += xAdvance; - tmpBlock.LastCharIndex++; - } - else if (layout.TextWrapping == TextWrapping::WrapWords) - { - if (lastWrapCharIndex != INVALID_INDEX) - { - // Skip moving twice for the same character - int32 lastLineLastCharIndex = outputLines.HasItems() && outputLines.Last().Blocks.HasItems() ? outputLines.Last().Blocks.Last().LastCharIndex : -10000; - if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2) - { - currentIndex = nextCharIndex; - lastMoveLine = moveLine; - continue; - } - - // Move line - const Char wrapChar = text[lastWrapCharIndex]; - moveLine = true; - moveBlock = tmpBlock.FirstCharIndex < lastWrapCharIndex; - - cursorX = lastWrapCharX; - if (StringUtils::IsWhitespace(wrapChar)) - { - // Skip whitespaces - tmpBlock.LastCharIndex = lastWrapCharIndex - 1; - nextCharIndex = currentIndex = lastWrapCharIndex + 1; - } - else - { - tmpBlock.LastCharIndex = lastWrapCharIndex - 1; - nextCharIndex = currentIndex = lastWrapCharIndex; - } - } - } - else if (layout.TextWrapping == TextWrapping::WrapChars) - { - // Move line - moveLine = true; - moveBlock = tmpBlock.FirstCharIndex < currentChar; - nextCharIndex = currentIndex; - - // Skip moving twice for the same character - if (lastMoveLine) - break; - } - } - - if (moveBlock) { - // Add block - tmpBlock.Size.X = lastCursorX - cursorX; - tmpBlock.Size.Y = baseLinesDistanceScale * _fonts[currentFontIndex]->GetHeight(); - tmpBlock.LastCharIndex = Math::Max(tmpBlock.LastCharIndex, tmpBlock.FirstCharIndex); - tmpBlock.FontIndex = currentFontIndex; - tmpLine.Blocks.Add(tmpBlock); - - // Reset block - tmpBlock.Location.X = cursorX; - tmpBlock.FirstCharIndex = nextCharIndex; - tmpBlock.LastCharIndex = nextCharIndex - 1; - - currentFontIndex = nextFontIndex; - lastCursorX = cursorX; - } - - // Check if move to another line - if (moveLine) - { - // Add line - tmpLine.Size.X = cursorX; - tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - tmpLine.MaxAscender = maxAscender; - outputLines.Add(tmpLine); - - // Reset line - tmpLine.Blocks.Clear(); - tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; - cursorX = 0; - tmpBlock.Location.X = cursorX; - lastWrapCharIndex = INVALID_INDEX; - lastWrapCharX = 0; - previous.IsValid = false; - - // Reset max font height - maxHeight = 0; - maxAscender = 0; - lastCursorX = 0; - } - - currentIndex = nextCharIndex; - lastMoveLine = moveLine; - } - - // Check if an additional line should be created - if (text[textLength - 1] == '\n') - { - // Add line - tmpLine.Size.X = cursorX; - tmpLine.Size.Y = baseLinesDistanceScale * maxHeight; - outputLines.Add(tmpLine); - - tmpLine.Location.Y += baseLinesDistanceScale * maxHeight; - } - - // Check amount of lines - if (outputLines.IsEmpty()) - return; - - float totalHeight = tmpLine.Location.Y; - - Float2 offset = Float2::Zero; - if (layout.VerticalAlignment == TextAlignment::Center) - { - offset.Y += (layout.Bounds.GetHeight() - totalHeight) * 0.5f; - } - else if (layout.VerticalAlignment == TextAlignment::Far) - { - offset.Y += layout.Bounds.GetHeight() - totalHeight; - } - for (int32 i = 0; i < outputLines.Count(); i++) - { - MultiFontLineCache& line = outputLines[i]; - Float2 rootPos = line.Location + offset; - - // Fix upper left line corner to match desire text alignment - if (layout.HorizontalAlignment == TextAlignment::Center) - { - rootPos.X += (layout.Bounds.GetWidth() - line.Size.X) * 0.5f; - } - else if (layout.HorizontalAlignment == TextAlignment::Far) - { - rootPos.X += layout.Bounds.GetWidth() - line.Size.X; - } - - line.Location = rootPos; - - // Align all blocks to center in case they have different heights - for (int32 j = 0; j < line.Blocks.Count(); j++) - { - MultiFontBlockCache& block = line.Blocks[j]; - block.Location.Y += (line.MaxAscender - _fonts[block.FontIndex]->GetAscender()) / 2; - } - } -} - -Float2 MultiFont::GetCharPosition(const StringView& text, int32 index, const TextLayoutOptions& layout) -{ - // Check if there is no need to do anything - if (text.IsEmpty()) - return layout.Bounds.Location; - - // Process text - Array lines; - ProcessText(text, lines, layout); - ASSERT(lines.HasItems()); - float scale = layout.Scale / FontManager::FontScale; - float baseLinesDistance = layout.BaseLinesGapScale * scale; - Float2 rootOffset = layout.Bounds.Location; - - // Find line with that position - FontCharacterEntry previous; - FontCharacterEntry entry; - for (int32 lineIndex = 0; lineIndex < lines.Count(); lineIndex++) - { - const MultiFontLineCache& line = lines[lineIndex]; - for (int32 blockIndex = 0; blockIndex < line.Blocks.Count(); blockIndex++) - { - const MultiFontBlockCache& block = line.Blocks[blockIndex]; - // Check if desire position is somewhere inside characters in line range - if (Math::IsInRange(index, block.FirstCharIndex, block.LastCharIndex)) - { - float x = line.Location.X + block.Location.X; - float y = line.Location.Y + block.Location.Y; - - // Check all characters in the line - for (int32 currentIndex = block.FirstCharIndex; currentIndex < index; currentIndex++) - { - // Cache current character - const Char currentChar = text[currentIndex]; - _fonts[block.FontIndex]->GetCharacter(currentChar, entry); - const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - - // Apply kerning - if (!isWhitespace && previous.IsValid) - { - x += _fonts[block.FontIndex]->GetKerning(previous.Character, entry.Character); - } - previous = entry; - - // Move - x += entry.AdvanceX * scale; - } - - // Upper left corner of the character - return rootOffset + Float2(x, y); - } - } - } - - // Position after last character in the last line - return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, lines.Last().Location.Y); -} - -int32 MultiFont::HitTestText(const StringView& text, const Float2& location, const TextLayoutOptions& layout) -{ - // Check if there is no need to do anything - if (text.Length() <= 0) - return 0; - - // Process text - Array lines; - ProcessText(text, lines, layout); - ASSERT(lines.HasItems()); - float scale = layout.Scale / FontManager::FontScale; - - // Offset position to match lines origin space - Float2 rootOffset = layout.Bounds.Location + lines.First().Location; - Float2 testPoint = location - rootOffset; - - // Get block which may intersect with the position (it's possible because lines have fixed height) - int32 lineIndex = 0; - while (lineIndex < lines.Count()) - { - if (lines[lineIndex].Location.Y + lines[lineIndex].Size.Y >= location.Y) { - break; - } - - lineIndex++; - } - lineIndex = Math::Clamp(lineIndex, 0, lines.Count() - 1); - const MultiFontLineCache& line = lines[lineIndex]; - - int32 blockIndex = 0; - while (blockIndex < line.Blocks.Count() - 1) - { - if (line.Location.X + line.Blocks[blockIndex + 1].Location.X >= location.X) { - break; - } - - blockIndex++; - } - const MultiFontBlockCache& block = line.Blocks[blockIndex]; - float x = line.Location.X; - - // Check all characters in the line to find hit point - FontCharacterEntry previous; - FontCharacterEntry entry; - int32 smallestIndex = INVALID_INDEX; - float dst, smallestDst = MAX_float; - for (int32 currentIndex = block.FirstCharIndex; currentIndex <= block.LastCharIndex; currentIndex++) - { - // Cache current character - const Char currentChar = text[currentIndex]; - - _fonts[block.FontIndex]->GetCharacter(currentChar, entry); - const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - - // Apply kerning - if (!isWhitespace && previous.IsValid) - { - x += _fonts[block.FontIndex]->GetKerning(previous.Character, entry.Character); - } - previous = entry; - - // Test - dst = Math::Abs(testPoint.X - x); - if (dst < smallestDst) - { - // Found closer character - smallestIndex = currentIndex; - smallestDst = dst; - } - else if (dst > smallestDst) - { - // Current char is worse so return the best result - return smallestIndex; - } - - // Move - x += entry.AdvanceX * scale; - } - - // Test line end edge - dst = Math::Abs(testPoint.X - x); - if (dst < smallestDst) - { - // Pointer is behind the last character in the line - smallestIndex = block.LastCharIndex; - - // Fix for last line - if (lineIndex == lines.Count() - 1) - smallestIndex++; - } - - return smallestIndex; -} - -Float2 MultiFont::MeasureText(const StringView& text, const TextLayoutOptions& layout) -{ - // Check if there is no need to do anything - if (text.IsEmpty()) - return Float2::Zero; - - // Process text - Array lines; - ProcessText(text, lines, layout); - - // Calculate bounds - Float2 max = Float2::Zero; - for (int32 i = 0; i < lines.Count(); i++) - { - const MultiFontLineCache& line = lines[i]; - max = Float2::Max(max, line.Location + line.Size); - } - - return max; -} - diff --git a/Source/Engine/Render2D/MultiFont.h b/Source/Engine/Render2D/MultiFont.h deleted file mode 100644 index 870b8e668..000000000 --- a/Source/Engine/Render2D/MultiFont.h +++ /dev/null @@ -1,353 +0,0 @@ -#pragma once - -#include "Engine/Core/Collections/Array.h" -#include "Engine/Core/Collections/Dictionary.h" -#include "Font.h" -#include "FontAsset.h" - -struct TextRange; -class Font; -class FontAsset; - -/// -/// The font block info generated during text processing. -/// -API_STRUCT(NoDefault) struct MultiFontBlockCache -{ - DECLARE_SCRIPTING_TYPE_MINIMAL(MultiFontBlockCache); - - /// - /// The root position of the block (upper left corner), relative to line. - /// - API_FIELD() Float2 Location; - - /// - /// The height of the current block - /// - API_FIELD() Float2 Size; - - /// - /// The first character index (from the input text). - /// - API_FIELD() int32 FirstCharIndex; - - /// - /// The last character index (from the input text), inclusive. - /// - API_FIELD() int32 LastCharIndex; - - /// - /// The index of the font to render with - /// - API_FIELD() int32 FontIndex; -}; - -template<> -struct TIsPODType -{ - enum { Value = true }; -}; - -/// -/// Line of font blocks info generated during text processing. -/// -API_STRUCT(NoDefault) struct MultiFontLineCache -{ - DECLARE_SCRIPTING_TYPE_MINIMAL(MultiFontLineCache); - - /// - /// The root position of the line (upper left corner). - /// - API_FIELD() Float2 Location; - - /// - /// The line bounds (width and height). - /// - API_FIELD() Float2 Size; - - /// - /// The maximum ascender of the line. - /// - API_FIELD() float MaxAscender; - - /// - /// The index of the font to render with - /// - API_FIELD() Array Blocks; -}; - -API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API MultiFont : public ManagedScriptingObject -{ - DECLARE_SCRIPTING_TYPE_NO_SPAWN(MultiFont); -private: - Array _fonts; - -public: - MultiFont(const Array& fonts); - - API_FUNCTION() FORCE_INLINE static MultiFont* Create(const Array& fonts) { - return New(fonts); - } - - API_FUNCTION() FORCE_INLINE static MultiFont* Create(const Array& fontAssets, float size) { - Array fonts; - fonts.Resize(fontAssets.Count()); - for (int32 i = 0; i < fontAssets.Count(); i++) - { - fonts[i] = fontAssets[i]->CreateFont(size); - } - - return New(fonts); - } - - API_PROPERTY() FORCE_INLINE Array& GetFonts() { - return _fonts; - } - - API_PROPERTY() FORCE_INLINE void SetFonts(const Array& val) { - _fonts = val; - } - - API_PROPERTY() FORCE_INLINE int32 GetMaxHeight() { - int32 maxHeight = 0; - for (int32 i = 0; i < _fonts.Count(); i++) - { - if (_fonts[i]) { - maxHeight = Math::Max(maxHeight, _fonts[i]->GetHeight()); - } - } - - return maxHeight; - } - - API_PROPERTY() FORCE_INLINE int32 GetMaxAscender() { - int32 maxAsc = 0; - for (int32 i = 0; i < _fonts.Count(); i++) - { - if (_fonts[i]) { - maxAsc = Math::Max(maxAsc, _fonts[i]->GetAscender()); - } - } - - return maxAsc; - } - - /// - /// Processes text to get cached lines for rendering. - /// - /// The input text. - /// The layout properties. - /// The output lines list. - void ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Processes text to get cached lines for rendering. - /// - /// The input text. - /// The layout properties. - /// The output lines list. - API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) - { - Array lines; - ProcessText(text, lines, layout); - return lines; - } - - /// - /// Processes text to get cached lines for rendering. - /// - /// The input text. - /// The input text range (substring range of the input text parameter). - /// The layout properties. - /// The output lines list. - API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) - { - Array lines; - ProcessText(textRange.Substring(text), lines, layout); - return lines; - } - - /// - /// Processes text to get cached lines for rendering. - /// - /// The input text. - /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text) - { - return ProcessText(text, TextLayoutOptions()); - } - - /// - /// Processes text to get cached lines for rendering. - /// - /// The input text. - /// The input text range (substring range of the input text parameter). - /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange) - { - return ProcessText(textRange.Substring(text), TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text. - /// - /// The input text to test. - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return MeasureText(textRange.Substring(text), layout); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text - /// . - /// The input text to test. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text) - { - return MeasureText(text, TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text - /// . - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange) - { - return MeasureText(textRange.Substring(text), TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return HitTestText(textRange.Substring(text), location, layout); - } - - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, const Float2& location) - { - return HitTestText(text, location, TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) - { - return HitTestText(textRange.Substring(text), location, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index. - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Calculates character position for given text and character index. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return GetCharPosition(textRange.Substring(text), index, layout); - } - - /// - /// Calculates character position for given text and character index - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, int32 index) - { - return GetCharPosition(text, index, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) - { - return GetCharPosition(textRange.Substring(text), index, TextLayoutOptions()); - } - - /// - /// Gets the index of the font that should be used to render the char - /// - /// The font list. - /// The char. - /// Number to return if char cannot be found. - /// - API_FUNCTION() FORCE_INLINE int32 GetCharFontIndex(Char c, int32 missing = -1) { - int32 fontIndex = 0; - while (fontIndex < _fonts.Count() && _fonts[fontIndex] && !_fonts[fontIndex]->ContainsChar(c)) - { - fontIndex++; - } - - if (fontIndex == _fonts.Count()) { - return missing; - } - - return fontIndex; - } - - API_FUNCTION() FORCE_INLINE bool Verify() { - for (int32 i = 0; i < _fonts.Count(); i++) - { - if (!_fonts[i]) { - return false; - } - } - - return true; - } -}; diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index c68c2445d..4e0cacb44 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -3,7 +3,7 @@ #include "Render2D.h" #include "Font.h" #include "FontManager.h" -#include "MultiFont.h" +#include "FallbackFonts.h" #include "FontTextureAtlas.h" #include "RotatedRectangle.h" #include "SpriteAtlas.h" @@ -195,7 +195,7 @@ namespace // Drawing Array DrawCalls; Array Lines; - Array MultiFontLines; + Array BlockedTextLines; Array Lines2; bool IsScissorsRectEmpty; bool IsScissorsRectEnabled; @@ -1370,16 +1370,16 @@ void Render2D::DrawText(Font* font, const StringView& text, const TextRange& tex DrawText(font, textRange.Substring(text), color, layout, customMaterial); } -void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; - const Array& fonts = multiFont->GetFonts(); // Check if there is no need to do anything - if (fonts.IsEmpty() || text.Length() < 0) + if (font == nullptr || text.Length() < 0) return; // Temporary data + const Array& fallbackFonts = fallbacks->GetFontList(font->GetSize()); uint32 fontAtlasIndex = 0; FontTextureAtlas* fontAtlas = nullptr; Float2 invAtlasSize = Float2::One; @@ -1406,11 +1406,19 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo int32 lineIndex = 0; maxAscenders.Add(0); + + auto getFont = [&](int32 index)->Font* { + return index >= 0 ? fallbackFonts[index] : font; + }; + + // Preprocess the text to determine vertical offset of blocks for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) { - if (text[currentIndex] != '\n') { - int32 fontIndex = multiFont->GetCharFontIndex(text[currentIndex], 0); - maxAscenders[lineIndex] = Math::Max(maxAscenders[lineIndex], static_cast(fonts[fontIndex]->GetAscender())); + const Char c = text[currentIndex]; + if (c != '\n') { + int32 fontIndex = fallbacks->GetCharFallbackIndex(c, font); + maxAscenders[lineIndex] = Math::Max(maxAscenders[lineIndex], + static_cast(getFont(fontIndex)->GetAscender())); } else { lineIndex++; @@ -1424,7 +1432,7 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo // The starting index of the current block int32 startIndex = 0; // The index of the font used by the current block - int32 currentFontIndex = multiFont->GetCharFontIndex(text[0], 0); + int32 currentFontIndex = fallbacks->GetCharFallbackIndex(text[0], font); // The maximum font height of the current line float maxHeight = 0; for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++) @@ -1446,7 +1454,7 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo { // Get character entry if (nextCharIndex < text.Length()) { - nextFontIndex = multiFont->GetCharFontIndex(text[nextCharIndex], currentFontIndex); + nextFontIndex = fallbacks->GetCharFallbackIndex(text[nextCharIndex], font); } if (nextFontIndex != currentFontIndex) { @@ -1461,13 +1469,13 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo if (moveBlock) { // Render the pending block before beginning the new block - auto fontHeight = fonts[currentFontIndex]->GetHeight(); + auto fontHeight = getFont(currentFontIndex)->GetHeight(); maxHeight = Math::Max(maxHeight, static_cast(fontHeight)); - auto fontDescender = fonts[currentFontIndex]->GetDescender(); + auto fontDescender = getFont(currentFontIndex)->GetDescender(); for (int32 renderIndex = startIndex; renderIndex <= currentIndex; renderIndex++) { // Get character entry - fonts[currentFontIndex]->GetCharacter(text[renderIndex], entry); + getFont(currentFontIndex)->GetCharacter(text[renderIndex], entry); // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) @@ -1494,7 +1502,7 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo // Get kerning if (!isWhitespace && previous.IsValid) { - kerning = fonts[currentFontIndex]->GetKerning(previous.Character, entry.Character); + kerning = getFont(currentFontIndex)->GetKerning(previous.Character, entry.Character); } else { @@ -1510,7 +1518,7 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo const float x = pointer.X + entry.OffsetX * scale; const float y = pointer.Y + (fontHeight + fontDescender - entry.OffsetY) * scale; - Rectangle charRect(x, y + (maxAscenders[lineIndex] - fonts[currentFontIndex]->GetAscender()) / 2, entry.UVSize.X * scale, entry.UVSize.Y * scale); + Rectangle charRect(x, y + (maxAscenders[lineIndex] - getFont(currentFontIndex)->GetAscender()) / 2, entry.UVSize.X * scale, entry.UVSize.Y * scale); Float2 upperLeftUV = entry.UV * invAtlasSize; Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize; @@ -1541,21 +1549,21 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo } } -void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) { - DrawText(multiFont, textRange.Substring(text), color, location, customMaterial); + DrawText(font, fallbacks, textRange.Substring(text), color, location, customMaterial); } -void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; - const Array& fonts = multiFont->GetFonts(); // Check if there is no need to do anything - if (fonts.IsEmpty() || text.IsEmpty() || layout.Scale <= ZeroTolerance) + if (font == nullptr || text.IsEmpty() || layout.Scale <= ZeroTolerance) return; // Temporary data + const Array& fallbackFonts = fallbacks->GetFontList(font->GetSize()); uint32 fontAtlasIndex = 0; FontTextureAtlas* fontAtlas = nullptr; Float2 invAtlasSize = Float2::One; @@ -1564,8 +1572,8 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo float scale = layout.Scale / FontManager::FontScale; // Process text to get lines - MultiFontLines.Clear(); - multiFont->ProcessText(text, MultiFontLines, layout); + BlockedTextLines.Clear(); + font->ProcessText(fallbacks, text, BlockedTextLines, layout); // Render all lines FontCharacterEntry entry; @@ -1581,14 +1589,18 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo drawCall.AsChar.Mat = nullptr; } - for (int32 lineIndex = 0; lineIndex < MultiFontLines.Count(); lineIndex++) + auto getFont = [&](int32 index)->Font* { + return index >= 0 ? fallbackFonts[index] : font; + }; + + for (int32 lineIndex = 0; lineIndex < BlockedTextLines.Count(); lineIndex++) { - const MultiFontLineCache& line = MultiFontLines[lineIndex]; + const BlockedTextLineCache& line = BlockedTextLines[lineIndex]; for (int32 blockIndex = 0; blockIndex < line.Blocks.Count(); blockIndex++) { - const MultiFontBlockCache& block = MultiFontLines[lineIndex].Blocks[blockIndex]; - auto fontHeight = fonts[block.FontIndex]->GetHeight(); - auto fontDescender = fonts[block.FontIndex]->GetDescender(); + const FontBlockCache& block = BlockedTextLines[lineIndex].Blocks[blockIndex]; + auto fontHeight = getFont(block.FallbackFontIndex)->GetHeight(); + auto fontDescender = getFont(block.FallbackFontIndex)->GetDescender(); Float2 pointer = line.Location + block.Location; for (int32 charIndex = block.FirstCharIndex; charIndex <= block.LastCharIndex; charIndex++) @@ -1600,7 +1612,7 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo } // Get character entry - fonts[block.FontIndex]->GetCharacter(c, entry); + getFont(block.FallbackFontIndex)->GetCharacter(c, entry); // Check if need to select/change font atlas (since characters even in the same font may be located in different atlases) if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex) @@ -1625,7 +1637,7 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo const bool isWhitespace = StringUtils::IsWhitespace(c); if (!isWhitespace && previous.IsValid) { - kerning = fonts[block.FontIndex]->GetKerning(previous.Character, entry.Character); + kerning = getFont(block.FallbackFontIndex)->GetKerning(previous.Character, entry.Character); } else { @@ -1661,9 +1673,9 @@ void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const Colo } } -void Render2D::DrawText(MultiFont* multiFont, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { - DrawText(multiFont, textRange.Substring(text), color, layout, customMaterial); + DrawText(font, fallbacks, textRange.Substring(text), color, layout, customMaterial); } FORCE_INLINE bool NeedAlphaWithTint(const Color& color) diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index 5050171b2..c99d38104 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -15,7 +15,7 @@ struct Matrix3x3; struct Viewport; struct TextRange; class Font; -class MultiFont; +class FallbackFonts; class GPUPipelineState; class GPUTexture; class GPUTextureView; @@ -225,7 +225,7 @@ public: /// The text color. /// The text location. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// /// Draws a text with formatting. @@ -235,7 +235,7 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// /// Draws a text with formatting. @@ -246,7 +246,7 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// /// Draws a text with formatting. @@ -257,7 +257,7 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(MultiFont* multiFont, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// /// Fills a rectangle area. From cdbe59a3fbe4988660d9684b2fc9122ad2be4907 Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Thu, 30 Nov 2023 23:12:56 +0800 Subject: [PATCH 06/16] Add fallback settings to CSharp --- .../Content/Create/CreateFilesDialog.cs | 2 +- .../Content/Import/ImportFilesDialog.cs | 2 +- .../CustomEditors/Dedicated/RagdollEditor.cs | 2 +- .../CustomEditors/Dedicated/ScriptsEditor.cs | 2 +- .../Dedicated/UIControlEditor.cs | 6 +- .../Editors/ActorTransformEditor.cs | 2 +- .../Editor/CustomEditors/Editors/TagEditor.cs | 2 +- .../CustomEditors/LayoutElementsContainer.cs | 4 +- Source/Editor/GUI/ColumnDefinition.cs | 2 +- Source/Editor/GUI/ComboBox.cs | 6 +- .../GUI/ContextMenu/ContextMenuButton.cs | 4 +- Source/Editor/GUI/CurveEditor.cs | 2 +- Source/Editor/GUI/Docking/DockWindow.cs | 2 +- Source/Editor/GUI/MainMenu.cs | 8 +- Source/Editor/GUI/MainMenuButton.cs | 2 +- Source/Editor/GUI/NavigationButton.cs | 2 +- Source/Editor/GUI/Row.cs | 4 +- .../Editor/GUI/Timeline/Tracks/MemberTrack.cs | 2 +- Source/Editor/GUI/ToolStripButton.cs | 2 +- Source/Editor/GUI/Tree/TreeNode.cs | 10 +- Source/Editor/Options/InterfaceOptions.cs | 54 ++++---- Source/Editor/Options/OptionsModule.cs | 20 +-- .../Archetypes/Animation.StateMachine.cs | 4 +- .../Archetypes/Animation.TransitionEditor.cs | 2 +- Source/Editor/Surface/Archetypes/Animation.cs | 2 +- .../Editor/Surface/Archetypes/BehaviorTree.cs | 4 +- Source/Editor/Surface/Archetypes/Function.cs | 2 +- Source/Editor/Surface/AttributesEditor.cs | 2 +- .../Editor/Surface/ContextMenu/VisjectCM.cs | 2 +- .../Surface/ContextMenu/VisjectCMItem.cs | 2 +- Source/Editor/Surface/Elements/InputBox.cs | 4 +- Source/Editor/Surface/SurfaceNode.cs | 6 +- Source/Editor/Tools/Foliage/FoliageTab.cs | 2 +- Source/Editor/Tools/Terrain/CarveTab.cs | 2 +- .../Tools/Terrain/CreateTerrainDialog.cs | 2 +- Source/Editor/Utilities/TextRenderUtils.cs | 108 ++++++++++++++++ Source/Editor/Viewport/EditorViewport.cs | 6 +- .../Viewport/Widgets/ViewportWidgetButton.cs | 2 +- Source/Editor/Windows/AboutDialog.cs | 4 +- Source/Editor/Windows/Assets/FontWindow.cs | 2 +- Source/Editor/Windows/ContentWindow.Search.cs | 2 +- Source/Editor/Windows/PluginsWindow.cs | 9 +- Source/Editor/Windows/Profiler/Timeline.cs | 2 +- Source/Engine/Render2D/FallbackFonts.h | 2 +- Source/Engine/Render2D/MultiFontReference.cs | 74 ----------- Source/Engine/Render2D/Render2D.cs | 118 ++++++++++-------- Source/Engine/Scripting/Scripting.cs | 10 +- Source/Engine/UI/GUI/Common/Button.cs | 8 +- Source/Engine/UI/GUI/Common/Dropdown.cs | 8 +- Source/Engine/UI/GUI/Common/Label.cs | 14 +-- Source/Engine/UI/GUI/Common/RichTextBox.cs | 2 +- Source/Engine/UI/GUI/Common/TextBox.cs | 37 ++++-- Source/Engine/UI/GUI/Panels/DropPanel.cs | 6 +- Source/Engine/UI/GUI/Style.cs | 38 +++--- 54 files changed, 357 insertions(+), 273 deletions(-) create mode 100644 Source/Editor/Utilities/TextRenderUtils.cs delete mode 100644 Source/Engine/Render2D/MultiFontReference.cs diff --git a/Source/Editor/Content/Create/CreateFilesDialog.cs b/Source/Editor/Content/Create/CreateFilesDialog.cs index 48e4920bb..d48e878bc 100644 --- a/Source/Editor/Content/Create/CreateFilesDialog.cs +++ b/Source/Editor/Content/Create/CreateFilesDialog.cs @@ -39,7 +39,7 @@ namespace FlaxEditor.Content.Create AnchorPreset = AnchorPresets.HorizontalStretchTop, Offsets = new Margin(0, 0, 0, 40), Parent = this, - Font = new MultiFontReference(Style.Current.FontTitle) + Font = new FontReference(Style.Current.FontTitle) }; var infoLabel = new Label { diff --git a/Source/Editor/Content/Import/ImportFilesDialog.cs b/Source/Editor/Content/Import/ImportFilesDialog.cs index 5a142d0f6..967583cf6 100644 --- a/Source/Editor/Content/Import/ImportFilesDialog.cs +++ b/Source/Editor/Content/Import/ImportFilesDialog.cs @@ -60,7 +60,7 @@ namespace FlaxEditor.Content.Import AnchorPreset = AnchorPresets.HorizontalStretchTop, Offsets = new Margin(0, 0, 0, 40), Parent = this, - Font = new MultiFontReference(Style.Current.FontTitle) + Font = new FontReference(Style.Current.FontTitle) }; var infoLabel = new Label { diff --git a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs index b6d14d81e..c4b334b3a 100644 --- a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs @@ -81,7 +81,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new MultiFontReference(FlaxEngine.GUI.Style.Current.FontLarge), + Font = new FontReference(FlaxEngine.GUI.Style.Current.FontLarge), Text = "Ragdoll Options", Parent = this }; diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index fd56422cb..90ae9ae54 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -43,7 +43,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Add script button var buttonText = "Add script"; - var textSize = Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); float addScriptButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var buttonHeight = (textSize.Y < 18) ? 18 : textSize.Y + 4; _addScriptsButton = new Button diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index 9627edfd8..b8250918f 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -239,7 +239,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Title var title = new Label(2, 2, DialogWidth - 4, TitleHeight) { - Font = new MultiFontReference(style.FontLarge), + Font = new FontReference(style.FontLarge), Text = "Anchor Presets", Parent = this }; @@ -247,7 +247,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Info var info = new Label(0, title.Bottom, DialogWidth, InfoHeight) { - Font = new MultiFontReference(style.FontSmall), + Font = new FontReference(style.FontSmall), Text = "Shift: also set bounds\nControl: also set pivot", Parent = this }; @@ -423,7 +423,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Set control type button var space = layout.Space(20); var buttonText = "Set Type"; - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Render2D.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); float setTypeButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var setTypeButton = new Button { diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index 0cad200fd..e84bf5914 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -101,7 +101,7 @@ namespace FlaxEditor.CustomEditors.Editors _linkButton.Clicked += ToggleLink; ToggleEnabled(); SetLinkStyle(); - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(LinkedLabel.Text.Value); + var textSize = Render2D.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, LinkedLabel.Text.Value); _linkButton.LocalX += textSize.X + 10; LinkedLabel.SetupContextMenu += (label, menu, editor) => { diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs index dbd5d124c..49ac9d937 100644 --- a/Source/Editor/CustomEditors/Editors/TagEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs @@ -631,7 +631,7 @@ namespace FlaxEditor.CustomEditors.Editors TooltipText = "Edit...", Parent = _label, }; - var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Render2D.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); if (textSize.Y > button.Width) button.Width = textSize.Y + 2; diff --git a/Source/Editor/CustomEditors/LayoutElementsContainer.cs b/Source/Editor/CustomEditors/LayoutElementsContainer.cs index 1584e88de..936851b15 100644 --- a/Source/Editor/CustomEditors/LayoutElementsContainer.cs +++ b/Source/Editor/CustomEditors/LayoutElementsContainer.cs @@ -276,7 +276,7 @@ namespace FlaxEditor.CustomEditors public LabelElement Header(string text) { var element = Label(text); - element.Label.Font = new MultiFontReference(Style.Current.FontLarge); + element.Label.Font = new FontReference(Style.Current.FontLarge); return element; } @@ -284,7 +284,7 @@ namespace FlaxEditor.CustomEditors { var element = Header(header.Text); if (header.FontSize != -1) - element.Label.Font = new MultiFontReference(element.Label.Font, header.FontSize); + element.Label.Font = new FontReference(element.Label.Font.Font, header.FontSize); if (header.Color != 0) element.Label.TextColor = Color.FromRGBA(header.Color); return element; diff --git a/Source/Editor/GUI/ColumnDefinition.cs b/Source/Editor/GUI/ColumnDefinition.cs index c6e8f2889..aff1817c3 100644 --- a/Source/Editor/GUI/ColumnDefinition.cs +++ b/Source/Editor/GUI/ColumnDefinition.cs @@ -35,7 +35,7 @@ namespace FlaxEditor.GUI /// /// The title font. /// - public MultiFont TitleFont; + public Font TitleFont; /// /// The column title text color. diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index 8e6cf39a0..0417cc7e3 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -191,7 +191,7 @@ namespace FlaxEditor.GUI /// Gets or sets the font used to draw text. /// [EditorDisplay("Style"), EditorOrder(2000)] - public MultiFontReference Font { get; set; } + public FontReference Font { get; set; } /// /// Gets or sets the color of the text. @@ -273,7 +273,7 @@ namespace FlaxEditor.GUI MaximumItemsInViewCount = 20; var style = Style.Current; - Font = new MultiFontReference(style.FontMedium); + Font = new FontReference(style.FontMedium); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BackgroundColorHighlighted = BackgroundColor; @@ -554,7 +554,7 @@ namespace FlaxEditor.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetMultiFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + Render2D.DrawText(Font.GetFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); } diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index ba3326412..3137de240 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -236,9 +236,9 @@ namespace FlaxEditor.GUI.ContextMenu float width = 20; if (style.FontMedium) { - width += style.FontMedium.MeasureText(Text).X; + width += Render2D.MeasureText(style.FontMedium, Text).X; if (!string.IsNullOrEmpty(ShortKeys)) - width += 40 + style.FontMedium.MeasureText(ShortKeys).X; + width += 40 + Render2D.MeasureText(style.FontMedium, ShortKeys).X; } return Mathf.Max(width, base.MinimumWidth); diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index ff14cdb24..22deec120 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -317,7 +317,7 @@ namespace FlaxEditor.GUI private Color _contentsColor; private Color _linesColor; private Color _labelsColor; - private MultiFont _labelsFont; + private Font _labelsFont; /// /// The keyframe UI points. diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index 374885f01..36a4c112e 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -489,7 +489,7 @@ namespace FlaxEditor.GUI.Docking { var style = Style.Current; if (style?.FontMedium != null) - _titleSize = style.FontMedium.MeasureText(_title); + _titleSize = Render2D.MeasureText(style.FontMedium, _title); } base.PerformLayoutBeforeChildren(); diff --git a/Source/Editor/GUI/MainMenu.cs b/Source/Editor/GUI/MainMenu.cs index aadfbf0a7..b313fed9f 100644 --- a/Source/Editor/GUI/MainMenu.cs +++ b/Source/Editor/GUI/MainMenu.cs @@ -76,7 +76,7 @@ namespace FlaxEditor.GUI var windowIcon = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.WindowIcon); FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.WindowIconsFont); - MultiFont iconFont = new MultiFontReference([windowIconsFont], 9).GetMultiFont(); + Font iconFont = windowIconsFont?.CreateFont(9); _window = mainWindow.RootWindow.Window; _window.HitTest += OnHitTest; @@ -108,7 +108,7 @@ namespace FlaxEditor.GUI _closeButton = new Button { Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(), - Font = new MultiFontReference(iconFont), + Font = new FontReference(iconFont), BackgroundColor = Color.Transparent, BorderColor = Color.Transparent, BorderColorHighlighted = Color.Transparent, @@ -124,7 +124,7 @@ namespace FlaxEditor.GUI _minimizeButton = new Button { Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(), - Font = new MultiFontReference(iconFont), + Font = new FontReference(iconFont), BackgroundColor = Color.Transparent, BorderColor = Color.Transparent, BorderColorHighlighted = Color.Transparent, @@ -139,7 +139,7 @@ namespace FlaxEditor.GUI _maximizeButton = new Button { Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(), - Font = new MultiFontReference(iconFont), + Font = new FontReference(iconFont), BackgroundColor = Color.Transparent, BorderColor = Color.Transparent, BorderColorHighlighted = Color.Transparent, diff --git a/Source/Editor/GUI/MainMenuButton.cs b/Source/Editor/GUI/MainMenuButton.cs index 3bc57479c..3440996aa 100644 --- a/Source/Editor/GUI/MainMenuButton.cs +++ b/Source/Editor/GUI/MainMenuButton.cs @@ -103,7 +103,7 @@ namespace FlaxEditor.GUI float width = 18; if (style.FontMedium) - width += style.FontMedium.MeasureText(Text).X; + width += Render2D.MeasureText(style.FontMedium, Text).X; Width = width; } diff --git a/Source/Editor/GUI/NavigationButton.cs b/Source/Editor/GUI/NavigationButton.cs index 6fd17332c..6face21ef 100644 --- a/Source/Editor/GUI/NavigationButton.cs +++ b/Source/Editor/GUI/NavigationButton.cs @@ -68,7 +68,7 @@ namespace FlaxEditor.GUI if (style.FontMedium) { - Width = style.FontMedium.MeasureText(Text).X + 2 * DefaultMargin; + Width = Render2D.MeasureText(style.FontMedium, Text).X + 2 * DefaultMargin; } } } diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index 7533dfb17..ab74cbe96 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -39,8 +39,8 @@ namespace FlaxEditor.GUI { Depth = -1; - if (Height < Style.Current.FontMedium.MaxHeight) - Height = Style.Current.FontMedium.MaxHeight + 4; + if (Height < Style.Current.FontMedium.Height) + Height = Style.Current.FontMedium.Height + 4; } /// diff --git a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs index 63787df2c..b4433622e 100644 --- a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs @@ -345,7 +345,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks if (_previewValue != null) { // Based on Track.Draw for track text placement - var left = _xOffset + 16 + Style.Current.FontSmall.MeasureText(Title ?? Name).X; + var left = _xOffset + 16 + Render2D.MeasureText(Style.Current.FontSmall, Title ?? Name).X; if (Icon.IsValid) left += 18; if (IsExpanded) diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index d21fd5689..cf34fd36b 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -152,7 +152,7 @@ namespace FlaxEditor.GUI if (hasSprite) width += iconSize; if (!string.IsNullOrEmpty(_text) && style.FontMedium) - width += style.FontMedium.MeasureText(_text).X + (hasSprite ? DefaultMargin : 0); + width += Render2D.MeasureText(style.FontMedium, _text).X + (hasSprite ? DefaultMargin : 0); Width = width; } diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index acb67ea8f..7b47e5eb6 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -115,7 +115,7 @@ namespace FlaxEditor.GUI.Tree /// Gets or sets the font used to render text. /// [EditorDisplay("Style"), EditorOrder(2000)] - public MultiFontReference TextFont { get; set; } + public FontReference TextFont { get; set; } /// /// Gets or sets the color of the background when tree node is selected. @@ -318,7 +318,7 @@ namespace FlaxEditor.GUI.Tree BackgroundColorSelected = style.BackgroundSelected; BackgroundColorHighlighted = style.BackgroundHighlighted; BackgroundColorSelectedUnfocused = style.LightBackground; - TextFont = new MultiFontReference(style.FontSmall); + TextFont = new FontReference(style.FontSmall); } /// @@ -573,10 +573,10 @@ namespace FlaxEditor.GUI.Tree { if (_textChanged) { - var font = TextFont.GetMultiFont(); + var font = TextFont.GetFont(); if (font) { - _textWidth = font.MeasureText(_text).X; + _textWidth = Render2D.MeasureText(font, _text).X; _textChanged = false; } } @@ -657,7 +657,7 @@ namespace FlaxEditor.GUI.Tree } // Draw text - Render2D.DrawText(TextFont.GetMultiFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(TextFont.GetFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); // Draw drag and drop effect if (IsDragOver && _tree.DraggedOverNode == this) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 91eebe4eb..af432baa7 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -171,8 +171,10 @@ namespace FlaxEditor.Options get => _outputLogFont; set { - if (value == null || !value.Font) - _outputLogFont = new FontReference(ConsoleFont, 10); + if (value == null) + _outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont), 10); + else if (!value.Font) + _outputLogFont.Font = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont); else _outputLogFont = value; } @@ -234,30 +236,34 @@ namespace FlaxEditor.Options [EditorDisplay("Cook & Run"), EditorOrder(500)] public int NumberOfGameClientsToLaunch = 1; - private static FontAsset[] DefaultFonts => - [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont), - FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CjkFont)]; + private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); - private MultiFontReference _titleFont = new MultiFontReference(DefaultFonts, 18); - private MultiFontReference _largeFont = new MultiFontReference(DefaultFonts, 14); - private MultiFontReference _mediumFont = new MultiFontReference(DefaultFonts, 9); - private MultiFontReference _smallFont = new MultiFontReference(DefaultFonts, 9); + private FontReference _titleFont = new FontReference(DefaultFont, 18); + private FontReference _largeFont = new FontReference(DefaultFont, 14); + private FontReference _mediumFont = new FontReference(DefaultFont, 9); + private FontReference _smallFont = new FontReference(DefaultFont, 9); private FontReference _outputLogFont = new FontReference(ConsoleFont, 10); + /// + /// The fallback fonts. + /// + public FontAsset[] Fallbacks = [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.CjkFont)]; /// /// Gets or sets the title font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(600), Tooltip("The title font for editor UI.")] - public MultiFontReference TitleFont + public FontReference TitleFont { get => _titleFont; set { - if (value == null || !value.Verify()) - _titleFont = new MultiFontReference(DefaultFonts, 18); + if (value == null) + _titleFont = new FontReference(DefaultFont, 18); + else if (!value.Font) + _titleFont.Font = DefaultFont; else _titleFont = value; } @@ -267,13 +273,15 @@ namespace FlaxEditor.Options /// Gets or sets the large font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(610), Tooltip("The large font for editor UI.")] - public MultiFontReference LargeFont + public FontReference LargeFont { get => _largeFont; set { - if (value == null || !value.Verify()) - _largeFont = new MultiFontReference(DefaultFonts, 14); + if (value == null) + _largeFont = new FontReference(DefaultFont, 14); + else if (!value.Font) + _largeFont.Font = DefaultFont; else _largeFont = value; } @@ -283,13 +291,15 @@ namespace FlaxEditor.Options /// Gets or sets the medium font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(620), Tooltip("The medium font for editor UI.")] - public MultiFontReference MediumFont + public FontReference MediumFont { get => _mediumFont; set { - if (value == null || !value.Verify()) - _mediumFont = new MultiFontReference(DefaultFonts, 9); + if (value == null) + _mediumFont = new FontReference(DefaultFont, 9); + else if (!value.Font) + _mediumFont.Font = DefaultFont; else _mediumFont = value; } @@ -299,13 +309,15 @@ namespace FlaxEditor.Options /// Gets or sets the small font for editor UI. /// [EditorDisplay("Fonts"), EditorOrder(630), Tooltip("The small font for editor UI.")] - public MultiFontReference SmallFont + public FontReference SmallFont { get => _smallFont; set { - if (value == null || !value.Verify()) - _smallFont = new MultiFontReference(DefaultFonts, 9); + if (value == null) + _smallFont = new FontReference(DefaultFont, 9); + else if (!value.Font) + _smallFont.Font = DefaultFont; else _smallFont = value; } diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index d138d7d0d..b13548016 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -217,12 +217,14 @@ namespace FlaxEditor.Options if (styleName == ThemeOptions.LightDefault) { Style.Current = CreateLightStyle(); - } + } else { Style.Current = CreateDefaultStyle(); } } + + Render2D.Fallbacks = FallbackFonts.Create(Options.Interface.Fallbacks); } /// @@ -259,10 +261,10 @@ namespace FlaxEditor.Options }, // Fonts - FontTitle = options.Interface.TitleFont.GetMultiFont(), - FontLarge = options.Interface.LargeFont.GetMultiFont(), - FontMedium = options.Interface.MediumFont.GetMultiFont(), - FontSmall = options.Interface.SmallFont.GetMultiFont(), + FontTitle = options.Interface.TitleFont.GetFont(), + FontLarge = options.Interface.LargeFont.GetFont(), + FontMedium = options.Interface.MediumFont.GetFont(), + FontSmall = options.Interface.SmallFont.GetFont(), // Icons ArrowDown = Editor.Icons.ArrowDown12, @@ -312,10 +314,10 @@ namespace FlaxEditor.Options ProgressNormal = new Color(0.03f, 0.65f, 0.12f, 1f), // Fonts - FontTitle = options.Interface.TitleFont.GetMultiFont(), - FontLarge = options.Interface.LargeFont.GetMultiFont(), - FontMedium = options.Interface.MediumFont.GetMultiFont(), - FontSmall = options.Interface.SmallFont.GetMultiFont(), + FontTitle = options.Interface.TitleFont.GetFont(), + FontLarge = options.Interface.LargeFont.GetFont(), + FontMedium = options.Interface.MediumFont.GetFont(), + FontSmall = options.Interface.SmallFont.GetFont(), // Icons ArrowDown = Editor.Icons.ArrowDown12, diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index 621c7c25e..19a995b1e 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -337,7 +337,7 @@ namespace FlaxEditor.Surface.Archetypes _textRect = new Rectangle(Float2.Zero, Size); var style = Style.Current; - var titleSize = style.FontLarge.MeasureText(Title); + var titleSize = Render2D.MeasureText(style.FontLarge, Title); var width = Mathf.Max(100, titleSize.X + 50); Resize(width, 0); titleSize.X += 8.0f; @@ -1402,7 +1402,7 @@ namespace FlaxEditor.Surface.Archetypes { Title = StateTitle; var style = Style.Current; - var titleSize = style.FontLarge.MeasureText(Title); + var titleSize = Render2D.MeasureText(style.FontLarge, Title); var width = Mathf.Max(100, titleSize.X + 50); Resize(width, 0); titleSize.X += 8.0f; diff --git a/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs b/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs index e1fdb0a42..84c2144b2 100644 --- a/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs +++ b/Source/Editor/Surface/Archetypes/Animation.TransitionEditor.cs @@ -39,7 +39,7 @@ namespace FlaxEditor.Surface.Archetypes // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new MultiFontReference(Style.Current.FontLarge), + Font = new FontReference(Style.Current.FontLarge), Text = transition.SurfaceName, Parent = this }; diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index 40a3d2a63..dcf4a9689 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -77,7 +77,7 @@ namespace FlaxEditor.Surface.Archetypes Title = asset?.ShortName ?? "Animation"; var style = Style.Current; - Resize(Mathf.Max(230, style.FontLarge.MeasureText(Title).X + 30), 160); + Resize(Mathf.Max(230, Render2D.MeasureText(style.FontLarge, Title).X + 30), 160); } /// diff --git a/Source/Editor/Surface/Archetypes/BehaviorTree.cs b/Source/Editor/Surface/Archetypes/BehaviorTree.cs index cca6856ae..364dfa1ef 100644 --- a/Source/Editor/Surface/Archetypes/BehaviorTree.cs +++ b/Source/Editor/Surface/Archetypes/BehaviorTree.cs @@ -101,7 +101,7 @@ namespace FlaxEditor.Surface.Archetypes _debugRelevant = Behavior.GetNodeDebugRelevancy(instance, behavior); _debugInfo = Behavior.GetNodeDebugInfo(instance, behavior); if (!string.IsNullOrEmpty(_debugInfo)) - _debugInfoSize = Style.Current.FontSmall.MeasureText(_debugInfo); + _debugInfoSize = Render2D.MeasureText(Style.Current.FontSmall, _debugInfo); } } @@ -488,7 +488,7 @@ namespace FlaxEditor.Surface.Archetypes var height = 0.0f; var titleLabelFont = Style.Current.FontLarge; width = Mathf.Max(width, 100.0f); - width = Mathf.Max(width, titleLabelFont.MeasureText(Title).X + 30); + width = Mathf.Max(width, Render2D.MeasureText(titleLabelFont, Title).X + 30); if (_debugInfoSize.X > 0) { width = Mathf.Max(width, _debugInfoSize.X + 8.0f); diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index bc982a510..53950dad2 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -1407,7 +1407,7 @@ namespace FlaxEditor.Surface.Archetypes // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new MultiFontReference(Style.Current.FontLarge), + Font = new FontReference(Style.Current.FontLarge), Text = "Edit function signature", Parent = this }; diff --git a/Source/Editor/Surface/AttributesEditor.cs b/Source/Editor/Surface/AttributesEditor.cs index c9e32e23a..81b11bb68 100644 --- a/Source/Editor/Surface/AttributesEditor.cs +++ b/Source/Editor/Surface/AttributesEditor.cs @@ -83,7 +83,7 @@ namespace FlaxEditor.Surface // Title var title = new Label(2, 2, width - 4, 23.0f) { - Font = new MultiFontReference(Style.Current.FontLarge), + Font = new FontReference(Style.Current.FontLarge), Text = "Edit attributes", Parent = this }; diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 0624d44b3..5256b3cb7 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -141,7 +141,7 @@ namespace FlaxEditor.Surface.ContextMenu }; // Title bar - var titleFontReference = new MultiFontReference(Style.Current.FontLarge); + var titleFontReference = new FontReference(Style.Current.FontLarge); var titleLabel = new Label { Width = Width * 0.5f - 8f, diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 207875a92..fd9f0c63b 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -286,7 +286,7 @@ namespace FlaxEditor.Surface.ContextMenu Render2D.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); if (_archetype.SubTitle != null) { - var titleLength = style.FontSmall.MeasureText(_archetype.Title).X; + var titleLength = Render2D.MeasureText(style.FontSmall, _archetype.Title).X; var subTitleRect = new Rectangle(textRect.X + titleLength, textRect.Y, textRect.Width - titleLength, textRect.Height); Render2D.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } diff --git a/Source/Editor/Surface/Elements/InputBox.cs b/Source/Editor/Surface/Elements/InputBox.cs index 2047bcd1a..2ab286bd9 100644 --- a/Source/Editor/Surface/Elements/InputBox.cs +++ b/Source/Editor/Surface/Elements/InputBox.cs @@ -1428,7 +1428,7 @@ namespace FlaxEditor.Surface.Elements if (_defaultValueEditor != null) { - _defaultValueEditor.Location = new Float2(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y); + _defaultValueEditor.Location = new Float2(X + Width + 8 + Render2D.MeasureText(Style.Current.FontSmall, Text).X, Y); } } @@ -1635,7 +1635,7 @@ namespace FlaxEditor.Surface.Elements { if (DefaultValueEditors[i].CanUse(this, ref _currentType)) { - var bounds = new Rectangle(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y, 90, Height); + var bounds = new Rectangle(X + Width + 8 + Render2D.MeasureText(Style.Current.FontSmall, Text).X, Y, 90, Height); _editor = DefaultValueEditors[i]; try { diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index b6436e542..60d776102 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -200,7 +200,7 @@ namespace FlaxEditor.Surface continue; if (child is InputBox inputBox) { - var boxWidth = boxLabelFont.MeasureText(inputBox.Text).X + 20; + var boxWidth = Render2D.MeasureText(boxLabelFont, inputBox.Text).X + 20; if (inputBox.DefaultValueEditor != null) boxWidth += inputBox.DefaultValueEditor.Width + 4; leftWidth = Mathf.Max(leftWidth, boxWidth); @@ -208,7 +208,7 @@ namespace FlaxEditor.Surface } else if (child is OutputBox outputBox) { - rightWidth = Mathf.Max(rightWidth, boxLabelFont.MeasureText(outputBox.Text).X + 20); + rightWidth = Mathf.Max(rightWidth, Render2D.MeasureText(boxLabelFont, outputBox.Text).X + 20); rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } else if (child is Control control) @@ -226,7 +226,7 @@ namespace FlaxEditor.Surface } } width = Mathf.Max(width, leftWidth + rightWidth + 10); - width = Mathf.Max(width, titleLabelFont.MeasureText(Title).X + 30); + width = Mathf.Max(width, Render2D.MeasureText(titleLabelFont, Title).X + 30); height = Mathf.Max(height, Mathf.Max(leftHeight, rightHeight)); Resize(width, height); } diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index 1badb8c0c..58cf6f204 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -148,7 +148,7 @@ namespace FlaxEditor.Tools.Foliage Parent = _noFoliagePanel, Enabled = false }; - var textSize = Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); if (_createNewFoliage.Width < textSize.X) { _createNewFoliage.LocalX -= (textSize.X - _createNewFoliage.Width) / 2; diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index 4ff85ca23..12c9fd148 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -106,7 +106,7 @@ namespace FlaxEditor.Tools.Terrain Parent = _noTerrainPanel, Enabled = false }; - var textSize = Style.Current.FontMedium.MeasureText(buttonText); + var textSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); if (_createTerrainButton.Width < textSize.X) { _createTerrainButton.LocalX -= (textSize.X - _createTerrainButton.Width) / 2; diff --git a/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs b/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs index cba52283d..252891d44 100644 --- a/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs +++ b/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs @@ -96,7 +96,7 @@ namespace FlaxEditor.Tools.Terrain AnchorPreset = AnchorPresets.HorizontalStretchTop, Offsets = new Margin(0, 0, 0, 40), Parent = this, - Font = new MultiFontReference(Style.Current.FontTitle) + Font = new FontReference(Style.Current.FontTitle) }; var infoLabel = new Label { diff --git a/Source/Editor/Utilities/TextRenderUtils.cs b/Source/Editor/Utilities/TextRenderUtils.cs new file mode 100644 index 000000000..d7b79ae81 --- /dev/null +++ b/Source/Editor/Utilities/TextRenderUtils.cs @@ -0,0 +1,108 @@ +using FlaxEngine.GUI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FlaxEngine +{ + public static class TextRenderUtils + { + /// + /// Draws a text using the global fallback defined in styles. + /// + /// The font to use. + /// The text to render. + /// The size and position of the area in which the text is drawn. + /// The text color. + /// The horizontal alignment of the text in a layout rectangle. + /// The vertical alignment of the text in a layout rectangle. + /// Describes how wrap text inside a layout rectangle. + /// The scale for distance one baseline from another. Default is 1. + /// The text drawing scale. Default is 1. + public static void DrawTextWithFallback(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + { + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + + + Render2D.DrawText(font, Style.Current.Fallbacks, text, color, ref layout); + } + + /// + /// Draws a text using the global fallback defined in styles. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). + /// + /// The font to use. + /// Custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. + /// The text to render. + /// The size and position of the area in which the text is drawn. + /// The text color. + /// The horizontal alignment of the text in a layout rectangle. + /// The vertical alignment of the text in a layout rectangle. + /// Describes how wrap text inside a layout rectangle. + /// The scale for distance one baseline from another. Default is 1. + /// The text drawing scale. Default is 1. + public static void DrawTextWithFallback(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + { + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + + Render2D.DrawText(font, Style.Current.Fallbacks, text, color, ref layout, customMaterial); + } + + public static Float2 MeasureTextWithFallback(Font font, string text) + { + return font.MeasureText(Style.Current.Fallbacks, text); + } + + public static Float2 MeasureTextWithFallback(Font font, string text, ref TextRange textRange) + { + return font.MeasureText(Style.Current.Fallbacks, text, ref textRange); + } + + public static Float2 MeasureTextWithFallback(Font font, string text, ref TextLayoutOptions layout) + { + return font.MeasureText(Style.Current.Fallbacks, text, ref layout); + } + + public static Float2 MeasureTextWithFallback(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout) + { + return font.MeasureText(Style.Current.Fallbacks, text, ref textRange, ref layout); + } + + public static Float2 GetCharPositionWithFallback(Font font, string text, int index) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, index); + } + + public static Float2 GetCharPositionWithFallback(Font font, string text, ref TextRange textRange, int index) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index); + } + + public static Float2 GetCharPositionWithFallback(Font font, string text, int index, ref TextLayoutOptions layout) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, index, ref layout); + } + + public static Float2 GetCharPositionWithFallback(Font font, string text, ref TextRange textRange, int index, ref TextLayoutOptions layout) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index, ref layout); + } + } +} diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index c49392d01..77a677c0c 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -548,9 +548,9 @@ namespace FlaxEditor.Viewport #region Camera settings widget var largestText = "Relative Panning"; - var textSize = Style.Current.FontMedium.MeasureText(largestText); + var textSize = Render2D.MeasureText(Style.Current.FontMedium, largestText); var xLocationForExtras = textSize.X + 5; - var cameraSpeedTextWidth = Style.Current.FontMedium.MeasureText("0.00").X; + var cameraSpeedTextWidth = Render2D.MeasureText(Style.Current.FontMedium, "0.00").X; // Camera Settings Widget _cameraWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); @@ -801,7 +801,7 @@ namespace FlaxEditor.Viewport #region View mode widget largestText = "Brightness"; - textSize = Style.Current.FontMedium.MeasureText(largestText); + textSize = Render2D.MeasureText(Style.Current.FontMedium, largestText); xLocationForExtras = textSize.X + 5; var viewMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft); diff --git a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs index fe73048fd..250790621 100644 --- a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs +++ b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs @@ -164,7 +164,7 @@ namespace FlaxEditor.Viewport.Widgets var style = Style.Current; if (style != null && style.FontMedium) - Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.MeasureText(_text).X, Icon.IsValid); + Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : Render2D.MeasureText(style.FontMedium, _text).X, Icon.IsValid); } } } diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index 1a81a9421..ac887dadf 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -41,7 +41,7 @@ namespace FlaxEditor.Windows var nameLabel = new Label(icon.Right + 10, icon.Top, 200, 34) { Text = "Flax Engine", - Font = new MultiFontReference(Style.Current.FontTitle), + Font = new FontReference(Style.Current.FontTitle), HorizontalAlignment = TextAlignment.Near, VerticalAlignment = TextAlignment.Center, Parent = this @@ -54,7 +54,7 @@ namespace FlaxEditor.Windows Parent = this }; var buttonText = "Copy version info"; - var fontSize = Style.Current.FontMedium.MeasureText(buttonText); + var fontSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); var copyVersionButton = new Button(Width - fontSize.X - 8, 6, fontSize.X + 4, 20) { Text = buttonText, diff --git a/Source/Editor/Windows/Assets/FontWindow.cs b/Source/Editor/Windows/Assets/FontWindow.cs index 2e2e14d99..bc91ca5e2 100644 --- a/Source/Editor/Windows/Assets/FontWindow.cs +++ b/Source/Editor/Windows/Assets/FontWindow.cs @@ -144,7 +144,7 @@ namespace FlaxEditor.Windows.Assets protected override void OnAssetLinked() { Asset.WaitForLoaded(); - _textPreview.Font = new MultiFontReference([Asset], 30); + _textPreview.Font = new FontReference(Asset, 30); _inputText.Text = string.Format("This is a sample text using font {0}.", Asset.FamilyName); var options = Asset.Options; _proxy.Set(ref options); diff --git a/Source/Editor/Windows/ContentWindow.Search.cs b/Source/Editor/Windows/ContentWindow.Search.cs index f29dc0bd6..a1072d158 100644 --- a/Source/Editor/Windows/ContentWindow.Search.cs +++ b/Source/Editor/Windows/ContentWindow.Search.cs @@ -57,7 +57,7 @@ namespace FlaxEditor.Windows var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetMultiFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + Render2D.DrawText(Font.GetFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); // Arrow diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index d6c14f4e4..4dfa0ed08 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -79,7 +79,7 @@ namespace FlaxEditor.Windows HorizontalAlignment = TextAlignment.Near, AnchorPreset = AnchorPresets.HorizontalStretchTop, Text = desc.Name, - Font = new MultiFontReference(Style.Current.FontLarge), + Font = new FontReference(Style.Current.FontLarge), Parent = this, Bounds = new Rectangle(tmp1, margin, Width - tmp1 - margin, 28), }; @@ -120,9 +120,9 @@ namespace FlaxEditor.Windows url = desc.HomepageUrl; else if (!string.IsNullOrEmpty(desc.RepositoryUrl)) url = desc.RepositoryUrl; - versionLabel.Font.ForEach(x => x.Font.WaitForLoaded()); - var font = versionLabel.Font.GetMultiFont(); - var authorWidth = font.MeasureText(desc.Author).X + 8; + versionLabel.Font.Font.WaitForLoaded(); + var font = versionLabel.Font.GetFont(); + var authorWidth = Render2D.MeasureText(font, desc.Author).X + 8; var authorLabel = new ClickableLabel { HorizontalAlignment = TextAlignment.Far, @@ -392,7 +392,6 @@ namespace FlaxEditor.Windows } Editor.Log("Plugin project has been cloned."); - try { // Start git submodule clone diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index 917647f23..00365f74a 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -86,7 +86,7 @@ namespace FlaxEditor.Windows.Profiler Render2D.DrawRectangle(bounds, color * 0.5f); if (_nameLength < 0 && style.FontMedium) - _nameLength = style.FontMedium.MeasureText(_name).X; + _nameLength = Render2D.MeasureText(style.FontMedium, _name).X; if (_nameLength < bounds.Width + 4) { diff --git a/Source/Engine/Render2D/FallbackFonts.h b/Source/Engine/Render2D/FallbackFonts.h index 4b97e1c11..5861354fc 100644 --- a/Source/Engine/Render2D/FallbackFonts.h +++ b/Source/Engine/Render2D/FallbackFonts.h @@ -39,7 +39,7 @@ public: /// /// Combine the primary fonts with the fallback fonts to get a font list /// - API_PROPERTY() FORCE_INLINE Array& GetFontList(float size) { + API_FUNCTION() FORCE_INLINE Array& GetFontList(float size) { Array* result; if (_cache.TryGet(size, result)) { return *result; diff --git a/Source/Engine/Render2D/MultiFontReference.cs b/Source/Engine/Render2D/MultiFontReference.cs deleted file mode 100644 index dd3f4d179..000000000 --- a/Source/Engine/Render2D/MultiFontReference.cs +++ /dev/null @@ -1,74 +0,0 @@ - - -using System.Collections.Generic; -using System.Linq; - -namespace FlaxEngine -{ - /// - /// Reference to multiple font references - /// - public class MultiFontReference : List - { - public MultiFontReference() - { - _cachedFont = null; - } - - public MultiFontReference(IEnumerable other) - { - AddRange(other); - _cachedFont = null; - } - - public MultiFontReference(MultiFontReference other) - { - AddRange(other); - _cachedFont = other._cachedFont; - } - - public MultiFontReference(MultiFontReference other, float size) - { - AddRange(other.Select(x => new FontReference(x) { Size = size })); - _cachedFont = null; - } - - public MultiFontReference(MultiFont other) - { - AddRange(other.Fonts.Select(x => new FontReference(x))); - _cachedFont = other; - } - - public MultiFontReference(FontAsset[] assets, float size) - { - AddRange(assets.Select(x => new FontReference(x, size))); - _cachedFont = null; - } - - [EditorOrder(0), Tooltip("The font asset to use as characters source.")] - public MultiFont GetMultiFont() - { - if (_cachedFont) - return _cachedFont; - var fontList = this.Where(x => x.Font).Select(x => x.GetFont()).ToArray(); - _cachedFont = MultiFont.Create(fontList); - return _cachedFont; - } - - public bool Verify() - { - foreach (var i in this) - { - if (!i.Font) - { - return false; - } - } - - return true; - } - - [NoSerialize] - private MultiFont _cachedFont; - } -} diff --git a/Source/Engine/Render2D/Render2D.cs b/Source/Engine/Render2D/Render2D.cs index cad8380ab..b7158ccc7 100644 --- a/Source/Engine/Render2D/Render2D.cs +++ b/Source/Engine/Render2D/Render2D.cs @@ -1,11 +1,17 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using FlaxEngine.GUI; using System; namespace FlaxEngine { partial class Render2D { + public static FallbackFonts Fallbacks + { + get; set; + } = null; + /// /// Pushes transformation layer. /// @@ -111,7 +117,8 @@ namespace FlaxEngine /// Describes how wrap text inside a layout rectangle. /// The scale for distance one baseline from another. Default is 1. /// The text drawing scale. Default is 1. - public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + /// Whether to use fallback fonts for chars not renderable by the font. + public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) { var layout = new TextLayoutOptions { @@ -122,7 +129,15 @@ namespace FlaxEngine Scale = scale, BaseLinesGapScale = baseLinesGapScale, }; - DrawText(font, text, color, ref layout); + + if (useFallback && Fallbacks != null) + { + DrawText(font, Fallbacks, text, color, ref layout); + } + else + { + DrawText(font, text, color, ref layout); + } } /// @@ -138,7 +153,8 @@ namespace FlaxEngine /// Describes how wrap text inside a layout rectangle. /// The scale for distance one baseline from another. Default is 1. /// The text drawing scale. Default is 1. - public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + /// Whether to use fallback fonts for chars not renderable by the font. + public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) { var layout = new TextLayoutOptions { @@ -149,63 +165,63 @@ namespace FlaxEngine Scale = scale, BaseLinesGapScale = baseLinesGapScale, }; - DrawText(font, text, color, ref layout, customMaterial); + + if (useFallback && Fallbacks != null) + { + DrawText(font, Fallbacks, text, color, ref layout, customMaterial); + } + else + { + DrawText(font, text, color, ref layout, customMaterial); + } } - /// - /// Draws a text. - /// - /// The fonts to use, ordered by priority. - /// The text to render. - /// The size and position of the area in which the text is drawn. - /// The text color. - /// The horizontal alignment of the text in a layout rectangle. - /// The vertical alignment of the text in a layout rectangle. - /// Describes how wrap text inside a layout rectangle. - /// The scale for distance one baseline from another. Default is 1. - /// The text drawing scale. Default is 1. - public static void DrawText(MultiFont fonts, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + public static Float2 MeasureText(Font font, string text, bool useFallback = true) { - var layout = new TextLayoutOptions + if (useFallback && Fallbacks != null) { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; - - - DrawText(fonts, text, color, ref layout); + return font.MeasureText(Style.Current.Fallbacks, text); + } + else + { + return font.MeasureText(text); + } } - /// - /// Draws a text using a custom material shader. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). - /// - /// The fonts to use, ordered by priority. - /// Custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - /// The text to render. - /// The size and position of the area in which the text is drawn. - /// The text color. - /// The horizontal alignment of the text in a layout rectangle. - /// The vertical alignment of the text in a layout rectangle. - /// Describes how wrap text inside a layout rectangle. - /// The scale for distance one baseline from another. Default is 1. - /// The text drawing scale. Default is 1. - public static void DrawText(MultiFont fonts, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + public static Float2 MeasureText(Font font, string text, ref TextRange textRange, bool useFallback = true) { - var layout = new TextLayoutOptions + if (useFallback && Fallbacks != null) { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; + return font.MeasureText(Style.Current.Fallbacks, text, ref textRange); + } + else + { + return font.MeasureText(text, ref textRange); + } + } - DrawText(fonts, text, color, ref layout, customMaterial); + public static Float2 MeasureText(Font font, string text, ref TextLayoutOptions layout, bool useFallback = true) + { + if (useFallback && Fallbacks != null) + { + return font.MeasureText(Style.Current.Fallbacks, text, ref layout); + } + else + { + return font.MeasureText(text, ref layout); + } + } + + public static Float2 MeasureText(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout, bool useFallback = true) + { + if (useFallback && Fallbacks != null) + { + return font.MeasureText(Style.Current.Fallbacks, text, ref textRange, ref layout); + } + else + { + return font.MeasureText(text, ref textRange, ref layout); + } } /// diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index bc800f88a..a4cfd76b1 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -294,12 +294,12 @@ namespace FlaxEngine style.DragWindow = style.BackgroundSelected * 0.7f; // Use optionally bundled default font (matches Editor) - FontAsset[] defaultFont = [Content.LoadAsyncInternal("Editor/Fonts/Roboto-Regular"), Content.LoadAsyncInternal("Editor/Fonts/NotoSansSC-Regular")]; + FontAsset defaultFont = Content.LoadAsyncInternal("Editor/Fonts/Roboto-Regular"); - style.FontTitle = new MultiFontReference(defaultFont, 18).GetMultiFont(); - style.FontLarge = new MultiFontReference(defaultFont, 14).GetMultiFont(); - style.FontMedium = new MultiFontReference(defaultFont, 9).GetMultiFont(); - style.FontSmall = new MultiFontReference(defaultFont, 9).GetMultiFont(); + style.FontTitle = new FontReference(defaultFont, 18).GetFont(); + style.FontLarge = new FontReference(defaultFont, 14).GetFont(); + style.FontMedium = new FontReference(defaultFont, 9).GetFont(); + style.FontSmall = new FontReference(defaultFont, 9).GetFont(); Style.Current = style; } diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index f21a29191..756501c5e 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -23,7 +23,7 @@ namespace FlaxEngine.GUI /// /// The font. /// - protected MultiFontReference _font; + protected FontReference _font; /// /// The text. @@ -44,7 +44,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font used to draw button text. /// [EditorDisplay("Text Style"), EditorOrder(2022), ExpandGroups] - public MultiFontReference Font + public FontReference Font { get => _font; set => _font = value; @@ -156,7 +156,7 @@ namespace FlaxEngine.GUI var style = Style.Current; if (style != null) { - _font = new MultiFontReference(style.FontMedium); + _font = new FontReference(style.FontMedium); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BorderColor = style.BorderNormal; @@ -262,7 +262,7 @@ namespace FlaxEngine.GUI Render2D.DrawRectangle(clientRect, borderColor, BorderThickness); // Draw text - Render2D.DrawText(_font?.GetMultiFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); } /// diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 2681aac1b..c9fef131c 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -278,7 +278,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font used to draw text. /// [EditorDisplay("Text Style"), EditorOrder(2021)] - public MultiFontReference Font { get; set; } + public FontReference Font { get; set; } /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. @@ -359,7 +359,7 @@ namespace FlaxEngine.GUI : base(0, 0, 120, 18.0f) { var style = Style.Current; - Font = new MultiFontReference(style.FontMedium); + Font = new FontReference(style.FontMedium); TextColor = style.Foreground; BackgroundColor = style.BackgroundNormal; BackgroundColorHighlighted = BackgroundColor; @@ -476,7 +476,7 @@ namespace FlaxEngine.GUI var font = Font.GetFont(); for (int i = 0; i < _items.Count; i++) { - itemsWidth = Mathf.Max(itemsWidth, itemsMargin + 4 + font.MeasureText(_items[i]).X); + itemsWidth = Mathf.Max(itemsWidth, itemsMargin + 4 + Render2D.MeasureText(font, _items[i]).X); } */ var itemsWidth = Width; @@ -674,7 +674,7 @@ namespace FlaxEngine.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetMultiFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(Font.GetFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); } diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index ccc1cc190..d7147d3ab 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -26,7 +26,7 @@ namespace FlaxEngine.GUI /// /// The font. /// - protected MultiFontReference _font; + protected FontReference _font; /// /// Gets or sets the text. @@ -86,7 +86,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font. /// [EditorDisplay("Text Style"), EditorOrder(2024)] - public MultiFontReference Font + public FontReference Font { get => _font; set @@ -192,7 +192,7 @@ namespace FlaxEngine.GUI { AutoFocus = false; var style = Style.Current; - Font = new MultiFontReference(style.FontMedium); + Font = new FontReference(style.FontMedium); TextColor = style.Foreground; TextColorHighlighted = style.Foreground; } @@ -203,7 +203,7 @@ namespace FlaxEngine.GUI { AutoFocus = false; var style = Style.Current; - Font = new MultiFontReference(style.FontMedium); + Font = new FontReference(style.FontMedium); TextColor = style.Foreground; TextColorHighlighted = style.Foreground; } @@ -235,7 +235,7 @@ namespace FlaxEngine.GUI } } - Render2D.DrawText(_font.GetMultiFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); if (ClipText) Render2D.PopClip(); @@ -246,7 +246,7 @@ namespace FlaxEngine.GUI { if (_autoWidth || _autoHeight || _autoFitText) { - var font = _font.GetMultiFont(); + var font = _font.GetFont(); if (font) { // Calculate text size @@ -256,7 +256,7 @@ namespace FlaxEngine.GUI layout.Bounds.Size.X = Width - Margin.Width; else if (_autoWidth && !_autoHeight) layout.Bounds.Size.Y = Height - Margin.Height; - _textSize = font.MeasureText(_text, ref layout); + _textSize = Render2D.MeasureText(font, _text, ref layout); _textSize.Y *= BaseLinesGapScale; // Check if size is controlled via text diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.cs b/Source/Engine/UI/GUI/Common/RichTextBox.cs index f8ac7d353..514878b37 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.cs @@ -46,7 +46,7 @@ namespace FlaxEngine.GUI var style = Style.Current; _textStyle = new TextBlockStyle { - Font = new FontReference(style.FontMedium.Fonts.First()), + Font = new FontReference(style.FontMedium), Color = style.Foreground, BackgroundSelectedBrush = new SolidColorBrush(style.BackgroundSelected), }; diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index d9c7c1a80..a32f3a76c 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System.Drawing; using System.Linq; namespace FlaxEngine.GUI @@ -40,7 +41,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font. /// [EditorDisplay("Text Style"), EditorOrder(2024)] - public MultiFontReference Font { get; set; } + public FontReference Font { get; set; } /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. @@ -90,7 +91,7 @@ namespace FlaxEngine.GUI _layout.Bounds = new Rectangle(DefaultMargin, 1, Width - 2 * DefaultMargin, Height - 2); var style = Style.Current; - Font = new MultiFontReference(style.FontMedium); + Font = new FontReference(style.FontMedium); TextColor = style.Foreground; WatermarkTextColor = style.ForegroundDisabled; SelectionColor = style.BackgroundSelected; @@ -99,33 +100,33 @@ namespace FlaxEngine.GUI /// public override Float2 GetTextSize() { - var font = Font.GetMultiFont(); + var font = Font.GetFont(); if (font == null) { return Float2.Zero; } - return font.MeasureText(_text, ref _layout); + return Render2D.MeasureText(font, _text, ref _layout); } /// public override Float2 GetCharPosition(int index, out float height) { - var font = Font.GetMultiFont(); + var font = Font.GetFont(); if (font == null) { height = Height; return Float2.Zero; } - height = font.MaxHeight / DpiScale; + height = font.Height / DpiScale; return font.GetCharPosition(_text, index, ref _layout); } /// public override int HitTestText(Float2 location) { - var font = Font.GetMultiFont(); + var font = Font.GetFont(); if (font == null) { return 0; @@ -148,7 +149,7 @@ namespace FlaxEngine.GUI // Cache data var rect = new Rectangle(Float2.Zero, Size); bool enabled = EnabledInHierarchy; - var font = Font.GetMultiFont(); + var font = Font.GetFont(); if (!font) return; @@ -172,7 +173,7 @@ namespace FlaxEngine.GUI { var leftEdge = font.GetCharPosition(_text, SelectionLeft, ref _layout); var rightEdge = font.GetCharPosition(_text, SelectionRight, ref _layout); - float fontHeight = font.MaxHeight / DpiScale; + float fontHeight = font.Height / DpiScale; // Draw selection background float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); @@ -212,11 +213,25 @@ namespace FlaxEngine.GUI var color = TextColor; if (!enabled) color *= 0.6f; - Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); + if (Render2D.Fallbacks != null) + { + Render2D.DrawText(font, Render2D.Fallbacks, _text, color, ref _layout, TextMaterial); + } + else + { + Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); + } } else if (!string.IsNullOrEmpty(_watermarkText) && !IsFocused) { - Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + if (Render2D.Fallbacks != null) + { + Render2D.DrawText(font, Render2D.Fallbacks, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + } + else + { + Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + } } // Caret diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index 06256c9f8..ed620ab98 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -130,7 +130,7 @@ namespace FlaxEngine.GUI /// Gets or sets the font used to render panel header text. /// [EditorDisplay("Header Text Style"), EditorOrder(2020), ExpandGroups] - public MultiFontReference HeaderTextFont { get; set; } + public FontReference HeaderTextFont { get; set; } /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. @@ -238,7 +238,7 @@ namespace FlaxEngine.GUI var style = Style.Current; HeaderColor = style.BackgroundNormal; HeaderColorMouseOver = style.BackgroundHighlighted; - HeaderTextFont = new MultiFontReference(style.FontMedium); + HeaderTextFont = new FontReference(style.FontMedium); HeaderTextColor = style.Foreground; ArrowImageOpened = new SpriteBrush(style.ArrowDown); ArrowImageClosed = new SpriteBrush(style.ArrowRight); @@ -375,7 +375,7 @@ namespace FlaxEngine.GUI textColor *= 0.6f; } - Render2D.DrawText(HeaderTextFont.GetMultiFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(HeaderTextFont.GetFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); if (!_isClosed && EnableContainmentLines) { diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index a8b87fb3a..c4b51b085 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -15,61 +15,67 @@ namespace FlaxEngine.GUI public static Style Current { get; set; } [Serialize] - private MultiFontReference _fontTitle; + private FontReference _fontTitle; /// /// The font title. /// [NoSerialize] [EditorOrder(10)] - public MultiFont FontTitle + public Font FontTitle { - get => _fontTitle?.GetMultiFont(); - set => _fontTitle = new MultiFontReference(value); + get => _fontTitle?.GetFont(); + set => _fontTitle = new FontReference(value); } [Serialize] - private MultiFontReference _fontLarge; + private FontReference _fontLarge; /// /// The font large. /// [NoSerialize] [EditorOrder(20)] - public MultiFont FontLarge + public Font FontLarge { - get => _fontLarge?.GetMultiFont(); - set => _fontLarge = new MultiFontReference(value); + get => _fontLarge?.GetFont(); + set => _fontLarge = new FontReference(value); } [Serialize] - private MultiFontReference _fontMedium; + private FontReference _fontMedium; /// /// The font medium. /// [NoSerialize] [EditorOrder(30)] - public MultiFont FontMedium + public Font FontMedium { - get => _fontMedium?.GetMultiFont(); - set => _fontMedium = new MultiFontReference(value); + get => _fontMedium?.GetFont(); + set => _fontMedium = new FontReference(value); } [Serialize] - private MultiFontReference _fontSmall; + private FontReference _fontSmall; /// /// The font small. /// [NoSerialize] [EditorOrder(40)] - public MultiFont FontSmall + public Font FontSmall { - get => _fontSmall?.GetMultiFont(); - set => _fontSmall = new MultiFontReference(value); + get => _fontSmall?.GetFont(); + set => _fontSmall = new FontReference(value); } + /// + /// The fallback fonts to use if the primary font can't render the char. + /// + [EditorOrder(50)] + public FallbackFonts Fallbacks; + /// /// The background color. /// From 623f478b4466bb248db1f6f7448124a1b338668b Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Thu, 30 Nov 2023 23:22:11 +0800 Subject: [PATCH 07/16] Move fallback rendering to new class --- .../CustomEditors/Dedicated/ScriptsEditor.cs | 2 +- .../Dedicated/UIControlEditor.cs | 2 +- .../Editors/ActorTransformEditor.cs | 2 +- .../Editor/CustomEditors/Editors/TagEditor.cs | 2 +- .../GUI/ContextMenu/ContextMenuButton.cs | 4 +- Source/Editor/GUI/Docking/DockWindow.cs | 2 +- Source/Editor/GUI/MainMenuButton.cs | 2 +- Source/Editor/GUI/NavigationButton.cs | 2 +- .../Editor/GUI/Timeline/Tracks/MemberTrack.cs | 2 +- Source/Editor/GUI/ToolStripButton.cs | 2 +- Source/Editor/GUI/Tree/TreeNode.cs | 2 +- .../Archetypes/Animation.StateMachine.cs | 4 +- Source/Editor/Surface/Archetypes/Animation.cs | 2 +- .../Editor/Surface/Archetypes/BehaviorTree.cs | 4 +- .../Surface/ContextMenu/VisjectCMItem.cs | 2 +- Source/Editor/Surface/Elements/InputBox.cs | 4 +- Source/Editor/Surface/SurfaceNode.cs | 6 +- Source/Editor/Tools/Foliage/FoliageTab.cs | 2 +- Source/Editor/Tools/Terrain/CarveTab.cs | 2 +- Source/Editor/Utilities/TextRenderUtils.cs | 108 --------------- Source/Editor/Viewport/EditorViewport.cs | 6 +- .../Viewport/Widgets/ViewportWidgetButton.cs | 2 +- Source/Editor/Windows/AboutDialog.cs | 2 +- Source/Editor/Windows/PluginsWindow.cs | 2 +- Source/Editor/Windows/Profiler/Timeline.cs | 2 +- Source/Engine/Render2D/FallbackTextUtils.cs | 126 ++++++++++++++++++ Source/Engine/Render2D/Render2D.cs | 78 +---------- Source/Engine/UI/GUI/Common/Dropdown.cs | 2 +- Source/Engine/UI/GUI/Common/Label.cs | 2 +- Source/Engine/UI/GUI/Common/TextBox.cs | 2 +- 30 files changed, 165 insertions(+), 217 deletions(-) delete mode 100644 Source/Editor/Utilities/TextRenderUtils.cs create mode 100644 Source/Engine/Render2D/FallbackTextUtils.cs diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 90ae9ae54..ab949508e 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -43,7 +43,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Add script button var buttonText = "Add script"; - var textSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); + var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); float addScriptButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var buttonHeight = (textSize.Y < 18) ? 18 : textSize.Y + 4; _addScriptsButton = new Button diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index b8250918f..4efbee259 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -423,7 +423,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Set control type button var space = layout.Space(20); var buttonText = "Set Type"; - var textSize = Render2D.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); + var textSize = FallbackTextUtils.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); float setTypeButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var setTypeButton = new Button { diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index e84bf5914..8889a765a 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -101,7 +101,7 @@ namespace FlaxEditor.CustomEditors.Editors _linkButton.Clicked += ToggleLink; ToggleEnabled(); SetLinkStyle(); - var textSize = Render2D.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, LinkedLabel.Text.Value); + var textSize = FallbackTextUtils.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, LinkedLabel.Text.Value); _linkButton.LocalX += textSize.X + 10; LinkedLabel.SetupContextMenu += (label, menu, editor) => { diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs index 49ac9d937..bbece7e04 100644 --- a/Source/Editor/CustomEditors/Editors/TagEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs @@ -631,7 +631,7 @@ namespace FlaxEditor.CustomEditors.Editors TooltipText = "Edit...", Parent = _label, }; - var textSize = Render2D.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); + var textSize = FallbackTextUtils.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); if (textSize.Y > button.Width) button.Width = textSize.Y + 2; diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index 3137de240..d8c492e46 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -236,9 +236,9 @@ namespace FlaxEditor.GUI.ContextMenu float width = 20; if (style.FontMedium) { - width += Render2D.MeasureText(style.FontMedium, Text).X; + width += FallbackTextUtils.MeasureText(style.FontMedium, Text).X; if (!string.IsNullOrEmpty(ShortKeys)) - width += 40 + Render2D.MeasureText(style.FontMedium, ShortKeys).X; + width += 40 + FallbackTextUtils.MeasureText(style.FontMedium, ShortKeys).X; } return Mathf.Max(width, base.MinimumWidth); diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index 36a4c112e..b7c12287f 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -489,7 +489,7 @@ namespace FlaxEditor.GUI.Docking { var style = Style.Current; if (style?.FontMedium != null) - _titleSize = Render2D.MeasureText(style.FontMedium, _title); + _titleSize = FallbackTextUtils.MeasureText(style.FontMedium, _title); } base.PerformLayoutBeforeChildren(); diff --git a/Source/Editor/GUI/MainMenuButton.cs b/Source/Editor/GUI/MainMenuButton.cs index 3440996aa..6fe8b98d3 100644 --- a/Source/Editor/GUI/MainMenuButton.cs +++ b/Source/Editor/GUI/MainMenuButton.cs @@ -103,7 +103,7 @@ namespace FlaxEditor.GUI float width = 18; if (style.FontMedium) - width += Render2D.MeasureText(style.FontMedium, Text).X; + width += FallbackTextUtils.MeasureText(style.FontMedium, Text).X; Width = width; } diff --git a/Source/Editor/GUI/NavigationButton.cs b/Source/Editor/GUI/NavigationButton.cs index 6face21ef..5d399da02 100644 --- a/Source/Editor/GUI/NavigationButton.cs +++ b/Source/Editor/GUI/NavigationButton.cs @@ -68,7 +68,7 @@ namespace FlaxEditor.GUI if (style.FontMedium) { - Width = Render2D.MeasureText(style.FontMedium, Text).X + 2 * DefaultMargin; + Width = FallbackTextUtils.MeasureText(style.FontMedium, Text).X + 2 * DefaultMargin; } } } diff --git a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs index b4433622e..dd79922ac 100644 --- a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs @@ -345,7 +345,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks if (_previewValue != null) { // Based on Track.Draw for track text placement - var left = _xOffset + 16 + Render2D.MeasureText(Style.Current.FontSmall, Title ?? Name).X; + var left = _xOffset + 16 + FallbackTextUtils.MeasureText(Style.Current.FontSmall, Title ?? Name).X; if (Icon.IsValid) left += 18; if (IsExpanded) diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index cf34fd36b..50dce78b4 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -152,7 +152,7 @@ namespace FlaxEditor.GUI if (hasSprite) width += iconSize; if (!string.IsNullOrEmpty(_text) && style.FontMedium) - width += Render2D.MeasureText(style.FontMedium, _text).X + (hasSprite ? DefaultMargin : 0); + width += FallbackTextUtils.MeasureText(style.FontMedium, _text).X + (hasSprite ? DefaultMargin : 0); Width = width; } diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 7b47e5eb6..700ba6da1 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -576,7 +576,7 @@ namespace FlaxEditor.GUI.Tree var font = TextFont.GetFont(); if (font) { - _textWidth = Render2D.MeasureText(font, _text).X; + _textWidth = FallbackTextUtils.MeasureText(font, _text).X; _textChanged = false; } } diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index 19a995b1e..078b89c56 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -337,7 +337,7 @@ namespace FlaxEditor.Surface.Archetypes _textRect = new Rectangle(Float2.Zero, Size); var style = Style.Current; - var titleSize = Render2D.MeasureText(style.FontLarge, Title); + var titleSize = FallbackTextUtils.MeasureText(style.FontLarge, Title); var width = Mathf.Max(100, titleSize.X + 50); Resize(width, 0); titleSize.X += 8.0f; @@ -1402,7 +1402,7 @@ namespace FlaxEditor.Surface.Archetypes { Title = StateTitle; var style = Style.Current; - var titleSize = Render2D.MeasureText(style.FontLarge, Title); + var titleSize = FallbackTextUtils.MeasureText(style.FontLarge, Title); var width = Mathf.Max(100, titleSize.X + 50); Resize(width, 0); titleSize.X += 8.0f; diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index dcf4a9689..dae16b425 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -77,7 +77,7 @@ namespace FlaxEditor.Surface.Archetypes Title = asset?.ShortName ?? "Animation"; var style = Style.Current; - Resize(Mathf.Max(230, Render2D.MeasureText(style.FontLarge, Title).X + 30), 160); + Resize(Mathf.Max(230, FallbackTextUtils.MeasureText(style.FontLarge, Title).X + 30), 160); } /// diff --git a/Source/Editor/Surface/Archetypes/BehaviorTree.cs b/Source/Editor/Surface/Archetypes/BehaviorTree.cs index 364dfa1ef..5f3c0b0a2 100644 --- a/Source/Editor/Surface/Archetypes/BehaviorTree.cs +++ b/Source/Editor/Surface/Archetypes/BehaviorTree.cs @@ -101,7 +101,7 @@ namespace FlaxEditor.Surface.Archetypes _debugRelevant = Behavior.GetNodeDebugRelevancy(instance, behavior); _debugInfo = Behavior.GetNodeDebugInfo(instance, behavior); if (!string.IsNullOrEmpty(_debugInfo)) - _debugInfoSize = Render2D.MeasureText(Style.Current.FontSmall, _debugInfo); + _debugInfoSize = FallbackTextUtils.MeasureText(Style.Current.FontSmall, _debugInfo); } } @@ -488,7 +488,7 @@ namespace FlaxEditor.Surface.Archetypes var height = 0.0f; var titleLabelFont = Style.Current.FontLarge; width = Mathf.Max(width, 100.0f); - width = Mathf.Max(width, Render2D.MeasureText(titleLabelFont, Title).X + 30); + width = Mathf.Max(width, FallbackTextUtils.MeasureText(titleLabelFont, Title).X + 30); if (_debugInfoSize.X > 0) { width = Mathf.Max(width, _debugInfoSize.X + 8.0f); diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index fd9f0c63b..019be78b6 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -286,7 +286,7 @@ namespace FlaxEditor.Surface.ContextMenu Render2D.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); if (_archetype.SubTitle != null) { - var titleLength = Render2D.MeasureText(style.FontSmall, _archetype.Title).X; + var titleLength = FallbackTextUtils.MeasureText(style.FontSmall, _archetype.Title).X; var subTitleRect = new Rectangle(textRect.X + titleLength, textRect.Y, textRect.Width - titleLength, textRect.Height); Render2D.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } diff --git a/Source/Editor/Surface/Elements/InputBox.cs b/Source/Editor/Surface/Elements/InputBox.cs index 2ab286bd9..526d85d15 100644 --- a/Source/Editor/Surface/Elements/InputBox.cs +++ b/Source/Editor/Surface/Elements/InputBox.cs @@ -1428,7 +1428,7 @@ namespace FlaxEditor.Surface.Elements if (_defaultValueEditor != null) { - _defaultValueEditor.Location = new Float2(X + Width + 8 + Render2D.MeasureText(Style.Current.FontSmall, Text).X, Y); + _defaultValueEditor.Location = new Float2(X + Width + 8 + FallbackTextUtils.MeasureText(Style.Current.FontSmall, Text).X, Y); } } @@ -1635,7 +1635,7 @@ namespace FlaxEditor.Surface.Elements { if (DefaultValueEditors[i].CanUse(this, ref _currentType)) { - var bounds = new Rectangle(X + Width + 8 + Render2D.MeasureText(Style.Current.FontSmall, Text).X, Y, 90, Height); + var bounds = new Rectangle(X + Width + 8 + FallbackTextUtils.MeasureText(Style.Current.FontSmall, Text).X, Y, 90, Height); _editor = DefaultValueEditors[i]; try { diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 60d776102..3c2381192 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -200,7 +200,7 @@ namespace FlaxEditor.Surface continue; if (child is InputBox inputBox) { - var boxWidth = Render2D.MeasureText(boxLabelFont, inputBox.Text).X + 20; + var boxWidth = FallbackTextUtils.MeasureText(boxLabelFont, inputBox.Text).X + 20; if (inputBox.DefaultValueEditor != null) boxWidth += inputBox.DefaultValueEditor.Width + 4; leftWidth = Mathf.Max(leftWidth, boxWidth); @@ -208,7 +208,7 @@ namespace FlaxEditor.Surface } else if (child is OutputBox outputBox) { - rightWidth = Mathf.Max(rightWidth, Render2D.MeasureText(boxLabelFont, outputBox.Text).X + 20); + rightWidth = Mathf.Max(rightWidth, FallbackTextUtils.MeasureText(boxLabelFont, outputBox.Text).X + 20); rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } else if (child is Control control) @@ -226,7 +226,7 @@ namespace FlaxEditor.Surface } } width = Mathf.Max(width, leftWidth + rightWidth + 10); - width = Mathf.Max(width, Render2D.MeasureText(titleLabelFont, Title).X + 30); + width = Mathf.Max(width, FallbackTextUtils.MeasureText(titleLabelFont, Title).X + 30); height = Mathf.Max(height, Mathf.Max(leftHeight, rightHeight)); Resize(width, height); } diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index 58cf6f204..c2b1fbe68 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -148,7 +148,7 @@ namespace FlaxEditor.Tools.Foliage Parent = _noFoliagePanel, Enabled = false }; - var textSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); + var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); if (_createNewFoliage.Width < textSize.X) { _createNewFoliage.LocalX -= (textSize.X - _createNewFoliage.Width) / 2; diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index 12c9fd148..0a86aab6f 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -106,7 +106,7 @@ namespace FlaxEditor.Tools.Terrain Parent = _noTerrainPanel, Enabled = false }; - var textSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); + var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); if (_createTerrainButton.Width < textSize.X) { _createTerrainButton.LocalX -= (textSize.X - _createTerrainButton.Width) / 2; diff --git a/Source/Editor/Utilities/TextRenderUtils.cs b/Source/Editor/Utilities/TextRenderUtils.cs deleted file mode 100644 index d7b79ae81..000000000 --- a/Source/Editor/Utilities/TextRenderUtils.cs +++ /dev/null @@ -1,108 +0,0 @@ -using FlaxEngine.GUI; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace FlaxEngine -{ - public static class TextRenderUtils - { - /// - /// Draws a text using the global fallback defined in styles. - /// - /// The font to use. - /// The text to render. - /// The size and position of the area in which the text is drawn. - /// The text color. - /// The horizontal alignment of the text in a layout rectangle. - /// The vertical alignment of the text in a layout rectangle. - /// Describes how wrap text inside a layout rectangle. - /// The scale for distance one baseline from another. Default is 1. - /// The text drawing scale. Default is 1. - public static void DrawTextWithFallback(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) - { - var layout = new TextLayoutOptions - { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; - - - Render2D.DrawText(font, Style.Current.Fallbacks, text, color, ref layout); - } - - /// - /// Draws a text using the global fallback defined in styles. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). - /// - /// The font to use. - /// Custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - /// The text to render. - /// The size and position of the area in which the text is drawn. - /// The text color. - /// The horizontal alignment of the text in a layout rectangle. - /// The vertical alignment of the text in a layout rectangle. - /// Describes how wrap text inside a layout rectangle. - /// The scale for distance one baseline from another. Default is 1. - /// The text drawing scale. Default is 1. - public static void DrawTextWithFallback(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) - { - var layout = new TextLayoutOptions - { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; - - Render2D.DrawText(font, Style.Current.Fallbacks, text, color, ref layout, customMaterial); - } - - public static Float2 MeasureTextWithFallback(Font font, string text) - { - return font.MeasureText(Style.Current.Fallbacks, text); - } - - public static Float2 MeasureTextWithFallback(Font font, string text, ref TextRange textRange) - { - return font.MeasureText(Style.Current.Fallbacks, text, ref textRange); - } - - public static Float2 MeasureTextWithFallback(Font font, string text, ref TextLayoutOptions layout) - { - return font.MeasureText(Style.Current.Fallbacks, text, ref layout); - } - - public static Float2 MeasureTextWithFallback(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout) - { - return font.MeasureText(Style.Current.Fallbacks, text, ref textRange, ref layout); - } - - public static Float2 GetCharPositionWithFallback(Font font, string text, int index) - { - return font.GetCharPosition(Style.Current.Fallbacks, text, index); - } - - public static Float2 GetCharPositionWithFallback(Font font, string text, ref TextRange textRange, int index) - { - return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index); - } - - public static Float2 GetCharPositionWithFallback(Font font, string text, int index, ref TextLayoutOptions layout) - { - return font.GetCharPosition(Style.Current.Fallbacks, text, index, ref layout); - } - - public static Float2 GetCharPositionWithFallback(Font font, string text, ref TextRange textRange, int index, ref TextLayoutOptions layout) - { - return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index, ref layout); - } - } -} diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 77a677c0c..0633d0ba9 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -548,9 +548,9 @@ namespace FlaxEditor.Viewport #region Camera settings widget var largestText = "Relative Panning"; - var textSize = Render2D.MeasureText(Style.Current.FontMedium, largestText); + var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, largestText); var xLocationForExtras = textSize.X + 5; - var cameraSpeedTextWidth = Render2D.MeasureText(Style.Current.FontMedium, "0.00").X; + var cameraSpeedTextWidth = FallbackTextUtils.MeasureText(Style.Current.FontMedium, "0.00").X; // Camera Settings Widget _cameraWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); @@ -801,7 +801,7 @@ namespace FlaxEditor.Viewport #region View mode widget largestText = "Brightness"; - textSize = Render2D.MeasureText(Style.Current.FontMedium, largestText); + textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, largestText); xLocationForExtras = textSize.X + 5; var viewMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft); diff --git a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs index 250790621..598f4ee32 100644 --- a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs +++ b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs @@ -164,7 +164,7 @@ namespace FlaxEditor.Viewport.Widgets var style = Style.Current; if (style != null && style.FontMedium) - Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : Render2D.MeasureText(style.FontMedium, _text).X, Icon.IsValid); + Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : FallbackTextUtils.MeasureText(style.FontMedium, _text).X, Icon.IsValid); } } } diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index ac887dadf..02aad16ec 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -54,7 +54,7 @@ namespace FlaxEditor.Windows Parent = this }; var buttonText = "Copy version info"; - var fontSize = Render2D.MeasureText(Style.Current.FontMedium, buttonText); + var fontSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); var copyVersionButton = new Button(Width - fontSize.X - 8, 6, fontSize.X + 4, 20) { Text = buttonText, diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index 4dfa0ed08..03beab2fa 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -122,7 +122,7 @@ namespace FlaxEditor.Windows url = desc.RepositoryUrl; versionLabel.Font.Font.WaitForLoaded(); var font = versionLabel.Font.GetFont(); - var authorWidth = Render2D.MeasureText(font, desc.Author).X + 8; + var authorWidth = FallbackTextUtils.MeasureText(font, desc.Author).X + 8; var authorLabel = new ClickableLabel { HorizontalAlignment = TextAlignment.Far, diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index 00365f74a..780b31a3b 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -86,7 +86,7 @@ namespace FlaxEditor.Windows.Profiler Render2D.DrawRectangle(bounds, color * 0.5f); if (_nameLength < 0 && style.FontMedium) - _nameLength = Render2D.MeasureText(style.FontMedium, _name).X; + _nameLength = FallbackTextUtils.MeasureText(style.FontMedium, _name).X; if (_nameLength < bounds.Width + 4) { diff --git a/Source/Engine/Render2D/FallbackTextUtils.cs b/Source/Engine/Render2D/FallbackTextUtils.cs new file mode 100644 index 000000000..eb676f0a9 --- /dev/null +++ b/Source/Engine/Render2D/FallbackTextUtils.cs @@ -0,0 +1,126 @@ + +namespace FlaxEngine +{ + /// + /// A collection of functions to handle text rendering with fallback font + /// + public static class FallbackTextUtils + { + public static FallbackFonts Fallbacks + { + get; set; + } = null; + + public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + { + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + + if (Fallbacks != null) + { + Render2D.DrawText(font, Fallbacks, text, color, ref layout); + } + else + { + Render2D.DrawText(font, text, color, ref layout); + } + } + + public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + { + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + + if (Fallbacks != null) + { + Render2D.DrawText(font, Fallbacks, text, color, ref layout, customMaterial); + } + else + { + Render2D.DrawText(font, text, color, ref layout, customMaterial); + } + } + + public static Float2 MeasureText(Font font, string text) + { + if (Fallbacks != null) + { + return font.MeasureText(Fallbacks, text); + } + else + { + return font.MeasureText(text); + } + } + + public static Float2 MeasureText(Font font, string text, ref TextRange textRange) + { + if (Fallbacks != null) + { + return font.MeasureText(Fallbacks, text, ref textRange); + } + else + { + return font.MeasureText(text, ref textRange); + } + } + + public static Float2 MeasureText(Font font, string text, ref TextLayoutOptions layout) + { + if (Fallbacks != null) + { + return font.MeasureText(Fallbacks, text, ref layout); + } + else + { + return font.MeasureText(text, ref layout); + } + } + + public static Float2 MeasureText(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout) + { + if (Fallbacks != null) + { + return font.MeasureText(Fallbacks, text, ref textRange, ref layout); + } + else + { + return font.MeasureText(text, ref textRange, ref layout); + } + } + + public static Float2 GetCharPosition(Font font, string text, int index) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, index); + } + + public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index); + } + + public static Float2 GetCharPosition(Font font, string text, int index, ref TextLayoutOptions layout) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, index, ref layout); + } + + public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index, ref TextLayoutOptions layout) + { + return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index, ref layout); + } + } +} diff --git a/Source/Engine/Render2D/Render2D.cs b/Source/Engine/Render2D/Render2D.cs index b7158ccc7..6e4f1dc1d 100644 --- a/Source/Engine/Render2D/Render2D.cs +++ b/Source/Engine/Render2D/Render2D.cs @@ -7,10 +7,6 @@ namespace FlaxEngine { partial class Render2D { - public static FallbackFonts Fallbacks - { - get; set; - } = null; /// /// Pushes transformation layer. @@ -117,8 +113,7 @@ namespace FlaxEngine /// Describes how wrap text inside a layout rectangle. /// The scale for distance one baseline from another. Default is 1. /// The text drawing scale. Default is 1. - /// Whether to use fallback fonts for chars not renderable by the font. - public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) + public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) { var layout = new TextLayoutOptions { @@ -129,15 +124,7 @@ namespace FlaxEngine Scale = scale, BaseLinesGapScale = baseLinesGapScale, }; - - if (useFallback && Fallbacks != null) - { - DrawText(font, Fallbacks, text, color, ref layout); - } - else - { - DrawText(font, text, color, ref layout); - } + DrawText(font, text, color, ref layout); } /// @@ -153,8 +140,7 @@ namespace FlaxEngine /// Describes how wrap text inside a layout rectangle. /// The scale for distance one baseline from another. Default is 1. /// The text drawing scale. Default is 1. - /// Whether to use fallback fonts for chars not renderable by the font. - public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) + public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) { var layout = new TextLayoutOptions { @@ -165,63 +151,7 @@ namespace FlaxEngine Scale = scale, BaseLinesGapScale = baseLinesGapScale, }; - - if (useFallback && Fallbacks != null) - { - DrawText(font, Fallbacks, text, color, ref layout, customMaterial); - } - else - { - DrawText(font, text, color, ref layout, customMaterial); - } - } - - public static Float2 MeasureText(Font font, string text, bool useFallback = true) - { - if (useFallback && Fallbacks != null) - { - return font.MeasureText(Style.Current.Fallbacks, text); - } - else - { - return font.MeasureText(text); - } - } - - public static Float2 MeasureText(Font font, string text, ref TextRange textRange, bool useFallback = true) - { - if (useFallback && Fallbacks != null) - { - return font.MeasureText(Style.Current.Fallbacks, text, ref textRange); - } - else - { - return font.MeasureText(text, ref textRange); - } - } - - public static Float2 MeasureText(Font font, string text, ref TextLayoutOptions layout, bool useFallback = true) - { - if (useFallback && Fallbacks != null) - { - return font.MeasureText(Style.Current.Fallbacks, text, ref layout); - } - else - { - return font.MeasureText(text, ref layout); - } - } - - public static Float2 MeasureText(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout, bool useFallback = true) - { - if (useFallback && Fallbacks != null) - { - return font.MeasureText(Style.Current.Fallbacks, text, ref textRange, ref layout); - } - else - { - return font.MeasureText(text, ref textRange, ref layout); - } + DrawText(font, text, color, ref layout, customMaterial); } /// diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index c9fef131c..46f05f516 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -476,7 +476,7 @@ namespace FlaxEngine.GUI var font = Font.GetFont(); for (int i = 0; i < _items.Count; i++) { - itemsWidth = Mathf.Max(itemsWidth, itemsMargin + 4 + Render2D.MeasureText(font, _items[i]).X); + itemsWidth = Mathf.Max(itemsWidth, itemsMargin + 4 + FallbackTextUtils.MeasureText(font, _items[i]).X); } */ var itemsWidth = Width; diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index d7147d3ab..4e26c1934 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -256,7 +256,7 @@ namespace FlaxEngine.GUI layout.Bounds.Size.X = Width - Margin.Width; else if (_autoWidth && !_autoHeight) layout.Bounds.Size.Y = Height - Margin.Height; - _textSize = Render2D.MeasureText(font, _text, ref layout); + _textSize = FallbackTextUtils.MeasureText(font, _text, ref layout); _textSize.Y *= BaseLinesGapScale; // Check if size is controlled via text diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index a32f3a76c..ea7523f48 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -106,7 +106,7 @@ namespace FlaxEngine.GUI return Float2.Zero; } - return Render2D.MeasureText(font, _text, ref _layout); + return FallbackTextUtils.MeasureText(font, _text, ref _layout); } /// From 3d139a241ab4720e64ea03aae082de284bd5d26b Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:28:29 +0800 Subject: [PATCH 08/16] Fix C# editor --- Source/Editor/Content/GUI/ContentView.cs | 2 +- Source/Editor/Content/Items/ContentItem.cs | 2 +- Source/Editor/Content/Tree/ContentTreeNode.cs | 5 +- .../Dedicated/MeshReferenceEditor.cs | 4 +- .../CustomEditors/Dedicated/ScriptsEditor.cs | 3 +- .../CustomEditors/Dedicated/SplineEditor.cs | 2 +- .../Editors/ActorTransformEditor.cs | 1 - .../Editors/FlaxObjectRefEditor.cs | 4 +- .../CustomEditors/Editors/TypeEditor.cs | 4 +- Source/Editor/GUI/AssetPicker.cs | 6 +- Source/Editor/GUI/ComboBox.cs | 2 +- .../GUI/ContextMenu/ContextMenuButton.cs | 5 +- Source/Editor/GUI/CurveEditor.cs | 2 +- .../Editor/GUI/Dialogs/ColorPickerDialog.cs | 22 +- Source/Editor/GUI/Docking/DockPanelProxy.cs | 4 +- Source/Editor/GUI/Docking/DockWindow.cs | 1 - Source/Editor/GUI/ItemsListContextMenu.cs | 7 +- Source/Editor/GUI/MainMenuButton.cs | 3 +- Source/Editor/GUI/NavigationButton.cs | 3 +- Source/Editor/GUI/Row.cs | 3 +- Source/Editor/GUI/StatusBar.cs | 2 +- Source/Editor/GUI/StyleValueEditor.cs | 2 +- Source/Editor/GUI/Table.cs | 3 +- Source/Editor/GUI/Tabs/Tabs.cs | 2 +- Source/Editor/GUI/Timeline/GUI/Background.cs | 2 +- .../Editor/GUI/Timeline/GUI/PositionHandle.cs | 1 - Source/Editor/GUI/Timeline/Track.cs | 2 +- Source/Editor/GUI/ToolStripButton.cs | 3 +- Source/Editor/GUI/Tree/TreeNode.cs | 3 +- Source/Editor/Options/OptionsModule.cs | 2 +- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 4 +- .../Archetypes/Animation.StateMachine.cs | 4 +- .../Editor/Surface/Archetypes/BehaviorTree.cs | 3 +- .../Surface/Archetypes/ParticleModules.cs | 2 +- Source/Editor/Surface/Archetypes/Particles.cs | 4 +- .../Editor/Surface/ContextMenu/VisjectCM.cs | 2 +- .../Surface/ContextMenu/VisjectCMItem.cs | 18 +- Source/Editor/Surface/Elements/InputBox.cs | 2 +- Source/Editor/Surface/Elements/OutputBox.cs | 2 +- Source/Editor/Surface/Elements/TextView.cs | 2 +- Source/Editor/Surface/SurfaceComment.cs | 2 +- Source/Editor/Surface/SurfaceNode.cs | 3 +- Source/Editor/Tools/Foliage/FoliageTab.cs | 1 - Source/Editor/Tools/Terrain/CarveTab.cs | 1 - Source/Editor/Viewport/EditorViewport.cs | 6 +- .../Viewport/Previews/AnimationPreview.cs | 4 +- .../Editor/Viewport/Previews/ModelPreview.cs | 4 +- .../Previews/ParticleSystemPreview.cs | 2 +- .../Viewport/Previews/SkinnedModelPreview.cs | 4 +- .../Viewport/Previews/TexturePreview.cs | 2 +- .../Viewport/Widgets/ViewportWidgetButton.cs | 3 +- Source/Editor/Windows/AboutDialog.cs | 1 - .../Windows/Assets/AnimationGraphWindow.cs | 2 +- .../Editor/Windows/Assets/AnimationWindow.cs | 2 +- Source/Editor/Windows/Assets/ModelWindow.cs | 4 +- .../Windows/Assets/SkinnedModelWindow.cs | 4 +- Source/Editor/Windows/ContentWindow.Search.cs | 2 +- Source/Editor/Windows/DebugLogWindow.cs | 4 +- Source/Editor/Windows/GameWindow.cs | 8 +- Source/Editor/Windows/Profiler/SingleChart.cs | 4 +- Source/Editor/Windows/Profiler/Timeline.cs | 5 +- Source/Editor/Windows/SceneTreeWindow.cs | 2 +- Source/Editor/Windows/ToolboxWindow.cs | 5 +- Source/Engine/Render2D/FallbackTextUtils.cs | 205 +++++++++++++----- Source/Engine/UI/GUI/Common/Button.cs | 3 +- Source/Engine/UI/GUI/Common/Dropdown.cs | 3 +- Source/Engine/UI/GUI/Common/Label.cs | 4 +- .../Engine/UI/GUI/Common/RichTextBox.Tags.cs | 1 - Source/Engine/UI/GUI/Common/RichTextBox.cs | 1 - .../Engine/UI/GUI/Common/RichTextBoxBase.cs | 8 +- Source/Engine/UI/GUI/Common/TextBox.cs | 28 +-- Source/Engine/UI/GUI/Panels/DropPanel.cs | 3 +- Source/Engine/UI/GUI/Style.cs | 1 - Source/Engine/UI/GUI/Tooltip.cs | 3 +- 74 files changed, 272 insertions(+), 213 deletions(-) diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs index 259be104b..1091c9cef 100644 --- a/Source/Editor/Content/GUI/ContentView.cs +++ b/Source/Editor/Content/GUI/ContentView.cs @@ -598,7 +598,7 @@ namespace FlaxEditor.Content.GUI // Check if it's an empty thing if (_items.Count == 0) { - Render2D.DrawText(style.FontSmall, IsSearching ? "No results" : "Empty", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, IsSearching ? "No results" : "Empty", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 604caa704..0842dfff7 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -745,7 +745,7 @@ namespace FlaxEditor.Content // Draw short name Render2D.PushClip(ref textRect); - Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f); + FallbackTextUtils.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f); Render2D.PopClip(); } diff --git a/Source/Editor/Content/Tree/ContentTreeNode.cs b/Source/Editor/Content/Tree/ContentTreeNode.cs index ee9b463f7..2f378e7f5 100644 --- a/Source/Editor/Content/Tree/ContentTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentTreeNode.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System.Collections.Generic; -using System.Linq; using FlaxEditor.GUI; using FlaxEditor.GUI.Drag; using FlaxEditor.GUI.Tree; @@ -151,8 +150,8 @@ namespace FlaxEditor.Content var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(text, ranges[i].StartIndex); - var end = font.GetCharPosition(text, ranges[i].EndIndex); + var start = FallbackTextUtils.GetCharPosition(font, text, ranges[i].StartIndex); + var end = FallbackTextUtils.GetCharPosition(font, text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs b/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs index 4099e5aee..66c1c791f 100644 --- a/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs @@ -109,7 +109,7 @@ namespace FlaxEditor.CustomEditors.Dedicated { // Draw name Render2D.PushClip(nameRect); - Render2D.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); // Draw deselect button @@ -118,7 +118,7 @@ namespace FlaxEditor.CustomEditors.Dedicated else { // Draw info - Render2D.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); } // Draw picker button diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index ab949508e..017fd7655 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -3,7 +3,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using FlaxEditor.Actions; using FlaxEditor.Content; using FlaxEditor.GUI; @@ -87,7 +86,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var size = Size; // Info - Render2D.DrawText(style.FontSmall, "Drag scripts here", new Rectangle(2, _addScriptsButton.Height + 4, size.X - 4, size.Y - 4 - 20), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, "Drag scripts here", new Rectangle(2, _addScriptsButton.Height + 4, size.X - 4, size.Y - 4 - 20), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); // Check if drag is over if (IsDragOver && _dragHandlers != null && _dragHandlers.HasValidDrag) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 7b9b65c5c..fa4379db1 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -226,7 +226,7 @@ namespace FlaxEditor.CustomEditors.Dedicated if (!enabled) color *= 0.6f; Render2D.DrawSprite(tab._customIcon, iconRect, color); - Render2D.DrawText(style.FontMedium, tab._customText, new Rectangle(0, iconSize, size.X, textHeight), color, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, tab._customText, new Rectangle(0, iconSize, size.X, textHeight), color, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index 8889a765a..d8d16027b 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -2,7 +2,6 @@ using FlaxEngine; using FlaxEngine.GUI; -using System.Linq; namespace FlaxEditor.CustomEditors.Editors { diff --git a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs index 731da3817..2f64ea159 100644 --- a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs @@ -199,7 +199,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Draw name Render2D.PushClip(nameRect); - Render2D.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); // Draw deselect button @@ -208,7 +208,7 @@ namespace FlaxEditor.CustomEditors.Editors else { // Draw info - Render2D.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); } // Draw picker button diff --git a/Source/Editor/CustomEditors/Editors/TypeEditor.cs b/Source/Editor/CustomEditors/Editors/TypeEditor.cs index 38800a738..4d0b90383 100644 --- a/Source/Editor/CustomEditors/Editors/TypeEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TypeEditor.cs @@ -160,13 +160,13 @@ namespace FlaxEditor.CustomEditors.Editors // Draw name Render2D.PushClip(nameRect); - Render2D.DrawText(style.FontMedium, _valueName, nameRect, style.Foreground, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, _valueName, nameRect, style.Foreground, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); } else { // Draw info - Render2D.DrawText(style.FontMedium, "-", nameRect, Color.OrangeRed, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "-", nameRect, Color.OrangeRed, TextAlignment.Near, TextAlignment.Center); } // Draw picker button diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs index 84f58daf1..2abe5db38 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -139,7 +139,7 @@ namespace FlaxEditor.GUI float sizeForTextLeft = Width - button1Rect.Right; if (sizeForTextLeft > 30) { - Render2D.DrawText( + FallbackTextUtils.DrawText( style.FontSmall, Validator.SelectedItem.ShortName, new Rectangle(button1Rect.Right + 2, 0, sizeForTextLeft, ButtonsSize), @@ -161,7 +161,7 @@ namespace FlaxEditor.GUI var name = Validator.SelectedAsset.GetType().Name; if (Validator.SelectedAsset.IsVirtual) name += " (virtual)"; - Render2D.DrawText( + FallbackTextUtils.DrawText( style.FontSmall, name, new Rectangle(button1Rect.Right + 2, 0, sizeForTextLeft, ButtonsSize), @@ -174,7 +174,7 @@ namespace FlaxEditor.GUI { // No element selected Render2D.FillRectangle(iconRect, style.BackgroundNormal); - Render2D.DrawText(style.FontMedium, "No asset\nselected", iconRect, Color.Orange, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize); + FallbackTextUtils.DrawText(style.FontMedium, "No asset\nselected", iconRect, Color.Orange, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize); } // Check if drag is over diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index 0417cc7e3..8f3a7cb4b 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -554,7 +554,7 @@ namespace FlaxEditor.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + FallbackTextUtils.DrawText(Font.GetFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); } diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index d8c492e46..035e997e9 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -129,12 +128,12 @@ namespace FlaxEditor.GUI.ContextMenu base.Draw(); // Draw text - Render2D.DrawText(style.FontMedium, Text, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, Text, textRect, textColor, TextAlignment.Near, TextAlignment.Center); if (!string.IsNullOrEmpty(ShortKeys)) { // Draw short keys - Render2D.DrawText(style.FontMedium, ShortKeys, textRect, textColor, TextAlignment.Far, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, ShortKeys, textRect, textColor, TextAlignment.Far, TextAlignment.Center); } // Draw icon diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index 22deec120..f4839acd5 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -832,7 +832,7 @@ namespace FlaxEditor.GUI 50, LabelsSize ); - Render2D.DrawText(_labelsFont, label, labelRect, _labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f); + FallbackTextUtils.DrawText(_labelsFont, label, labelRect, _labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f); } } } diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index 27878a763..8b1881faf 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -281,33 +281,33 @@ namespace FlaxEditor.GUI.Dialogs // RGBA var rgbaR = new Rectangle(_cRed.Left - ChannelTextWidth, _cRed.Y, 10000, _cRed.Height); - Render2D.DrawText(style.FontMedium, "R", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "R", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); rgbaR.Location.Y = _cGreen.Y; - Render2D.DrawText(style.FontMedium, "G", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "G", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); rgbaR.Location.Y = _cBlue.Y; - Render2D.DrawText(style.FontMedium, "B", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "B", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); rgbaR.Location.Y = _cAlpha.Y; - Render2D.DrawText(style.FontMedium, "A", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "A", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); // HSV left var hsvHl = new Rectangle(_cHue.Left - ChannelTextWidth, _cHue.Y, 10000, _cHue.Height); - Render2D.DrawText(style.FontMedium, "H", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "H", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); hsvHl.Location.Y = _cSaturation.Y; - Render2D.DrawText(style.FontMedium, "S", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "S", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); hsvHl.Location.Y = _cValue.Y; - Render2D.DrawText(style.FontMedium, "V", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "V", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); // HSV right var hsvHr = new Rectangle(_cHue.Right + 2, _cHue.Y, 10000, _cHue.Height); - Render2D.DrawText(style.FontMedium, "°", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "°", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); hsvHr.Location.Y = _cSaturation.Y; - Render2D.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); hsvHr.Location.Y = _cValue.Y; - Render2D.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); // Hex var hex = new Rectangle(_cHex.Left - 26, _cHex.Y, 10000, _cHex.Height); - Render2D.DrawText(style.FontMedium, "Hex", hex, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, "Hex", hex, textColor, TextAlignment.Near, TextAlignment.Center); // Color difference var newRect = new Rectangle(_cOK.X, _cHex.Bottom + PickerMargin, _cCancel.Right - _cOK.Left, 0); diff --git a/Source/Editor/GUI/Docking/DockPanelProxy.cs b/Source/Editor/GUI/Docking/DockPanelProxy.cs index e6e57de8e..0b36b85fa 100644 --- a/Source/Editor/GUI/Docking/DockPanelProxy.cs +++ b/Source/Editor/GUI/Docking/DockPanelProxy.cs @@ -208,7 +208,7 @@ namespace FlaxEditor.GUI.Docking } // Draw text - Render2D.DrawText( + FallbackTextUtils.DrawText( style.FontMedium, tab.Title, new Rectangle(DockPanel.DefaultLeftTextMargin + iconWidth, 0, Width - DockPanel.DefaultLeftTextMargin - DockPanel.DefaultButtonsSize - 2 * DockPanel.DefaultButtonsMargin, DockPanel.DefaultHeaderHeight), @@ -271,7 +271,7 @@ namespace FlaxEditor.GUI.Docking } // Draw text - Render2D.DrawText( + FallbackTextUtils.DrawText( style.FontMedium, tab.Title, new Rectangle(x + DockPanel.DefaultLeftTextMargin + iconWidth, 0, 10000, DockPanel.DefaultHeaderHeight), diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index b7c12287f..561c898a0 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -6,7 +6,6 @@ using FlaxEngine; using FlaxEngine.Assertions; using FlaxEngine.GUI; using FlaxEditor.Options; -using System.Linq; namespace FlaxEditor.GUI.Docking { diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index b823cc907..d3c433a47 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Input; using FlaxEditor.Utilities; @@ -87,8 +86,8 @@ namespace FlaxEditor.GUI var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(Name, ranges[i].StartIndex); - var end = font.GetCharPosition(Name, ranges[i].EndIndex); + var start = FallbackTextUtils.GetCharPosition(font, Name, ranges[i].StartIndex); + var end = FallbackTextUtils.GetCharPosition(font, Name, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + 2, 0, end.X - start.X, Height)); } Visible = true; @@ -137,7 +136,7 @@ namespace FlaxEditor.GUI } // Draw name - Render2D.DrawText(style.FontSmall, Name, textRect, TintColor * (Enabled ? style.Foreground : style.ForegroundDisabled), TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, Name, textRect, TintColor * (Enabled ? style.Foreground : style.ForegroundDisabled), TextAlignment.Near, TextAlignment.Center); } /// diff --git a/Source/Editor/GUI/MainMenuButton.cs b/Source/Editor/GUI/MainMenuButton.cs index 6fe8b98d3..43849f4ab 100644 --- a/Source/Editor/GUI/MainMenuButton.cs +++ b/Source/Editor/GUI/MainMenuButton.cs @@ -2,7 +2,6 @@ using FlaxEngine; using FlaxEngine.GUI; -using System.Linq; namespace FlaxEditor.GUI { @@ -73,7 +72,7 @@ namespace FlaxEditor.GUI } // Draw text - Render2D.DrawText(style.FontMedium, Text, clientRect, enabled && hasChildItems ? style.Foreground : style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, Text, clientRect, enabled && hasChildItems ? style.Foreground : style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } /// diff --git a/Source/Editor/GUI/NavigationButton.cs b/Source/Editor/GUI/NavigationButton.cs index 5d399da02..8dedf72fd 100644 --- a/Source/Editor/GUI/NavigationButton.cs +++ b/Source/Editor/GUI/NavigationButton.cs @@ -2,7 +2,6 @@ using FlaxEngine; using FlaxEngine.GUI; -using System.Linq; namespace FlaxEditor.GUI { @@ -58,7 +57,7 @@ namespace FlaxEditor.GUI } // Draw text - Render2D.DrawText(style.FontMedium, Text, textRect, EnabledInHierarchy ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, Text, textRect, EnabledInHierarchy ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } /// diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index ab74cbe96..c7be48626 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -99,7 +98,7 @@ namespace FlaxEditor.GUI rect.Width -= leftDepthMargin; Render2D.PushClip(rect); - Render2D.DrawText(style.FontMedium, text, rect, style.Foreground, column.CellAlignment, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, text, rect, style.Foreground, column.CellAlignment, TextAlignment.Center); Render2D.PopClip(); x += width; diff --git a/Source/Editor/GUI/StatusBar.cs b/Source/Editor/GUI/StatusBar.cs index f8f7ae839..7770af9a1 100644 --- a/Source/Editor/GUI/StatusBar.cs +++ b/Source/Editor/GUI/StatusBar.cs @@ -56,7 +56,7 @@ namespace FlaxEditor.GUI Render2D.DrawSprite(style.StatusBarSizeGrip, new Rectangle(Width - 12, 10, 12, 12), style.Foreground); // Draw status text - Render2D.DrawText(style.FontSmall, Text, new Rectangle(4, 0, Width - 20, Height), TextColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, Text, new Rectangle(4, 0, Width - 20, Height), TextColor, TextAlignment.Near, TextAlignment.Center); } } } diff --git a/Source/Editor/GUI/StyleValueEditor.cs b/Source/Editor/GUI/StyleValueEditor.cs index a89902177..db6696152 100644 --- a/Source/Editor/GUI/StyleValueEditor.cs +++ b/Source/Editor/GUI/StyleValueEditor.cs @@ -157,7 +157,7 @@ namespace FlaxEditor.GUI { Rectangle textRectangle = r; textRectangle.X = 4; - Render2D.DrawText(style.FontMedium, "No Style", textRectangle, style.Foreground); + FallbackTextUtils.DrawText(style.FontMedium, "No Style", textRectangle, style.Foreground); } } diff --git a/Source/Editor/GUI/Table.cs b/Source/Editor/GUI/Table.cs index 54656a6c2..2358557c3 100644 --- a/Source/Editor/GUI/Table.cs +++ b/Source/Editor/GUI/Table.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; using System.Runtime.CompilerServices; using FlaxEngine; using FlaxEngine.GUI; @@ -131,7 +130,7 @@ namespace FlaxEditor.GUI var style = Style.Current; var font = column.TitleFont ?? style.FontMedium; - Render2D.DrawText(font, column.Title, rect, column.TitleColor, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(font, column.Title, rect, column.TitleColor, TextAlignment.Center, TextAlignment.Center); if (columnIndex < _columns.Length - 1) { diff --git a/Source/Editor/GUI/Tabs/Tabs.cs b/Source/Editor/GUI/Tabs/Tabs.cs index 3c70363e7..5995c7ef5 100644 --- a/Source/Editor/GUI/Tabs/Tabs.cs +++ b/Source/Editor/GUI/Tabs/Tabs.cs @@ -98,7 +98,7 @@ namespace FlaxEditor.GUI.Tabs // Draw text if (!string.IsNullOrEmpty(Tab.Text)) { - Render2D.DrawText(style.FontMedium, Tab.Text, new Rectangle(tabRect.X + textOffset, tabRect.Y, tabRect.Width - textOffset, tabRect.Height), style.Foreground, Tabs.TabsTextHorizontalAlignment, Tabs.TabsTextVerticalAlignment); + FallbackTextUtils.DrawText(style.FontMedium, Tab.Text, new Rectangle(tabRect.X + textOffset, tabRect.Y, tabRect.Width - textOffset, tabRect.Height), style.Foreground, Tabs.TabsTextHorizontalAlignment, Tabs.TabsTextVerticalAlignment); } } } diff --git a/Source/Editor/GUI/Timeline/GUI/Background.cs b/Source/Editor/GUI/Timeline/GUI/Background.cs index b9eff562a..05c1121c0 100644 --- a/Source/Editor/GUI/Timeline/GUI/Background.cs +++ b/Source/Editor/GUI/Timeline/GUI/Background.cs @@ -301,7 +301,7 @@ namespace FlaxEditor.GUI.Timeline.GUI default: throw new ArgumentOutOfRangeException(); } var labelRect = new Rectangle(x + 2, -verticalLinesHeaderExtend * 0.8f + timeAxisHeaderOffset, 50, verticalLinesHeaderExtend); - Render2D.DrawText(style.FontSmall, labelText, labelRect, labelColor, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.8f); + FallbackTextUtils.DrawText(style.FontSmall, labelText, labelRect, labelColor, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.8f); } } } diff --git a/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs b/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs index 791fb7133..3f835a35f 100644 --- a/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs +++ b/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs @@ -2,7 +2,6 @@ using System; using System.Globalization; -using System.Linq; using FlaxEngine; using FlaxEngine.GUI; diff --git a/Source/Editor/GUI/Timeline/Track.cs b/Source/Editor/GUI/Timeline/Track.cs index f38917aa6..20be8f0c2 100644 --- a/Source/Editor/GUI/Timeline/Track.cs +++ b/Source/Editor/GUI/Timeline/Track.cs @@ -965,7 +965,7 @@ namespace FlaxEditor.GUI.Timeline } // Draw text - Render2D.DrawText(style.FontSmall, Title ?? Name, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, Title ?? Name, textRect, textColor, TextAlignment.Near, TextAlignment.Center); // Disabled overlay DrawDisabled = Mute || (ParentTrack != null && ParentTrack.DrawDisabled); diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index 50dce78b4..c4f4603e2 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -137,7 +136,7 @@ namespace FlaxEditor.GUI if (!string.IsNullOrEmpty(_text)) { textRect.Size.X = Width - DefaultMargin - textRect.Left; - Render2D.DrawText(style.FontMedium, _text, textRect, enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, _text, textRect, enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } } diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 700ba6da1..6680e3608 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; using FlaxEngine; using FlaxEngine.GUI; @@ -657,7 +656,7 @@ namespace FlaxEditor.GUI.Tree } // Draw text - Render2D.DrawText(TextFont.GetFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(TextFont.GetFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); // Draw drag and drop effect if (IsDragOver && _tree.DraggedOverNode == this) diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index b13548016..dc376a0a2 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -224,7 +224,7 @@ namespace FlaxEditor.Options } } - Render2D.Fallbacks = FallbackFonts.Create(Options.Interface.Fallbacks); + FallbackTextUtils.Fallbacks = FallbackFonts.Create(Options.Interface.Fallbacks); } /// diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index f64e46385..eefd7f4dd 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -142,8 +142,8 @@ namespace FlaxEditor.SceneGraph.GUI var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(text, ranges[i].StartIndex); - var end = font.GetCharPosition(text, ranges[i].EndIndex); + var start = FallbackTextUtils.GetCharPosition(font, text, ranges[i].StartIndex); + var end = FallbackTextUtils.GetCharPosition(font, text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index 078b89c56..be9a96a7d 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -379,7 +379,7 @@ namespace FlaxEditor.Surface.Archetypes } // Name - Render2D.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); } /// @@ -1128,7 +1128,7 @@ namespace FlaxEditor.Surface.Archetypes } // Name - Render2D.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); // Close button Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) ? style.Foreground : style.ForegroundGrey); diff --git a/Source/Editor/Surface/Archetypes/BehaviorTree.cs b/Source/Editor/Surface/Archetypes/BehaviorTree.cs index 5f3c0b0a2..b240685cc 100644 --- a/Source/Editor/Surface/Archetypes/BehaviorTree.cs +++ b/Source/Editor/Surface/Archetypes/BehaviorTree.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEditor.CustomEditors.Dedicated; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Drag; @@ -185,7 +184,7 @@ namespace FlaxEditor.Surface.Archetypes if (!string.IsNullOrEmpty(_debugInfo)) { var style = Style.Current; - Render2D.DrawText(style.FontSmall, _debugInfo, new Rectangle(4, _headerRect.Bottom + 4, _debugInfoSize), style.Foreground); + FallbackTextUtils.DrawText(style.FontSmall, _debugInfo, new Rectangle(4, _headerRect.Bottom + 4, _debugInfoSize), style.Foreground); } // Debug relevancy outline diff --git a/Source/Editor/Surface/Archetypes/ParticleModules.cs b/Source/Editor/Surface/Archetypes/ParticleModules.cs index e5be3d35d..6082e212e 100644 --- a/Source/Editor/Surface/Archetypes/ParticleModules.cs +++ b/Source/Editor/Surface/Archetypes/ParticleModules.cs @@ -113,7 +113,7 @@ namespace FlaxEditor.Surface.Archetypes var idx = (int)ModuleType; var headerRect = new Rectangle(0, 0, Width, 16.0f); //Render2D.FillRectangle(headerRect, Color.Red); - Render2D.DrawText(style.FontMedium, Title, headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, Title, headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); DrawChildren(); diff --git a/Source/Editor/Surface/Archetypes/Particles.cs b/Source/Editor/Surface/Archetypes/Particles.cs index 40b38f7ce..bf7828960 100644 --- a/Source/Editor/Surface/Archetypes/Particles.cs +++ b/Source/Editor/Surface/Archetypes/Particles.cs @@ -154,7 +154,7 @@ namespace FlaxEditor.Surface.Archetypes if (headerRect.Contains(mousePosition)) headerColor *= 1.07f; Render2D.FillRectangle(headerRect, headerColor); - Render2D.DrawText(style.FontLarge, Names[idx], headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, Names[idx], headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); DrawChildren(); } @@ -194,7 +194,7 @@ namespace FlaxEditor.Surface.Archetypes if (_headerRect.Contains(ref _mousePosition)) headerColor *= 1.07f; Render2D.FillRectangle(_headerRect, headerColor); - Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); DrawChildren(); diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 5256b3cb7..930741807 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -141,7 +141,7 @@ namespace FlaxEditor.Surface.ContextMenu }; // Title bar - var titleFontReference = new FontReference(Style.Current.FontLarge); + var titleFontReference = new FontReference(Style.Current.FontLarge.Asset, 10); var titleLabel = new Label { Width = Width * 0.5f - 8f, diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 019be78b6..f4976be67 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -200,8 +200,8 @@ namespace FlaxEditor.Surface.ContextMenu var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(_archetype.Title, ranges[i].StartIndex); - var end = font.GetCharPosition(_archetype.Title, ranges[i].EndIndex); + var start = FallbackTextUtils.GetCharPosition(font, _archetype.Title, ranges[i].StartIndex); + var end = FallbackTextUtils.GetCharPosition(font, _archetype.Title, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); if (ranges[i].StartIndex <= 0) @@ -222,8 +222,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = font.GetCharPosition(_archetype.Title, 0); - var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); + var start = FallbackTextUtils.GetCharPosition(font, _archetype.Title, 0); + var end = FallbackTextUtils.GetCharPosition(font, _archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); _isFullMatch = true; Visible = true; @@ -237,8 +237,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = font.GetCharPosition(_archetype.Title, 0); - var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); + var start = FallbackTextUtils.GetCharPosition(font, _archetype.Title, 0); + var end = FallbackTextUtils.GetCharPosition(font, _archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); Visible = true; @@ -283,19 +283,19 @@ namespace FlaxEditor.Surface.ContextMenu } // Draw name - Render2D.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); if (_archetype.SubTitle != null) { var titleLength = FallbackTextUtils.MeasureText(style.FontSmall, _archetype.Title).X; var subTitleRect = new Rectangle(textRect.X + titleLength, textRect.Y, textRect.Width - titleLength, textRect.Height); - Render2D.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } // Reset transform and draw score mark if (showScoreHit) { Render2D.PopTransform(); - Render2D.DrawText(style.FontSmall, "> ", textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, "> ", textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } } diff --git a/Source/Editor/Surface/Elements/InputBox.cs b/Source/Editor/Surface/Elements/InputBox.cs index 526d85d15..a5a99b932 100644 --- a/Source/Editor/Surface/Elements/InputBox.cs +++ b/Source/Editor/Surface/Elements/InputBox.cs @@ -1443,7 +1443,7 @@ namespace FlaxEditor.Surface.Elements // Draw text var style = Style.Current; var rect = new Rectangle(Width + 4, 0, 1410, Height); - Render2D.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } /// diff --git a/Source/Editor/Surface/Elements/OutputBox.cs b/Source/Editor/Surface/Elements/OutputBox.cs index 497e2001a..5fb123700 100644 --- a/Source/Editor/Surface/Elements/OutputBox.cs +++ b/Source/Editor/Surface/Elements/OutputBox.cs @@ -189,7 +189,7 @@ namespace FlaxEditor.Surface.Elements // Draw text var style = Style.Current; var rect = new Rectangle(-100, 0, 100 - 2, Height); - Render2D.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Far, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Far, TextAlignment.Center); } } } diff --git a/Source/Editor/Surface/Elements/TextView.cs b/Source/Editor/Surface/Elements/TextView.cs index 284497396..c52667fe3 100644 --- a/Source/Editor/Surface/Elements/TextView.cs +++ b/Source/Editor/Surface/Elements/TextView.cs @@ -25,7 +25,7 @@ namespace FlaxEditor.Surface.Elements var style = Style.Current; var color = Enabled ? style.Foreground : style.ForegroundDisabled; - Render2D.DrawText(style.FontSmall, Archetype.Text, new Rectangle(Float2.Zero, Size), color, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontSmall, Archetype.Text, new Rectangle(Float2.Zero, Size), color, TextAlignment.Near, TextAlignment.Center); } } } diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index aad45190e..26cc912ab 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -169,7 +169,7 @@ namespace FlaxEditor.Surface // Header Render2D.FillRectangle(_headerRect, headerColor); - Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); // Close button Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 3c2381192..df706faa6 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEditor.Scripting; using FlaxEditor.Surface.Elements; using FlaxEditor.Surface.Undo; @@ -1028,7 +1027,7 @@ namespace FlaxEditor.Surface if (_headerRect.Contains(ref _mousePosition)) headerColor *= 1.07f; Render2D.FillRectangle(_headerRect, headerColor); - Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); // Close button if ((Archetype.Flags & NodeFlags.NoCloseButton) == 0 && Surface.CanEdit) diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index c2b1fbe68..d36c3b6ed 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEditor.GUI.Tabs; using FlaxEditor.Modules; using FlaxEditor.SceneGraph.Actors; diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index 0a86aab6f..e64a364f0 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; using FlaxEditor.GUI.Tabs; using FlaxEditor.Modules; using FlaxEditor.SceneGraph.Actors; diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 0633d0ba9..515514131 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -1233,8 +1233,8 @@ namespace FlaxEditor.Viewport color = Color.Yellow; var text = string.Format("FPS: {0}", fps); var font = Style.Current.FontMedium; - Render2D.DrawText(font, text, new Rectangle(Float2.One, Size), Color.Black); - Render2D.DrawText(font, text, new Rectangle(Float2.Zero, Size), color); + FallbackTextUtils.DrawText(font, text, new Rectangle(Float2.One, Size), Color.Black); + FallbackTextUtils.DrawText(font, text, new Rectangle(Float2.Zero, Size), color); } } @@ -1814,7 +1814,7 @@ namespace FlaxEditor.Viewport { var bounds = new Rectangle(Float2.Zero, Size); Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f)); - Render2D.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Viewport/Previews/AnimationPreview.cs b/Source/Editor/Viewport/Previews/AnimationPreview.cs index 1679a36bc..c4ada83c4 100644 --- a/Source/Editor/Viewport/Previews/AnimationPreview.cs +++ b/Source/Editor/Viewport/Previews/AnimationPreview.cs @@ -96,11 +96,11 @@ namespace FlaxEditor.Viewport.Previews var skinnedModel = SkinnedModel; if (skinnedModel == null) { - Render2D.DrawText(style.FontLarge, "Missing Base Model", new Rectangle(Float2.Zero, Size), Color.Red, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapWords); + FallbackTextUtils.DrawText(style.FontLarge, "Missing Base Model", new Rectangle(Float2.Zero, Size), Color.Red, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapWords); } else if (!skinnedModel.IsLoaded) { - Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Viewport/Previews/ModelPreview.cs b/Source/Editor/Viewport/Previews/ModelPreview.cs index a6496aafe..b31776bf6 100644 --- a/Source/Editor/Viewport/Previews/ModelPreview.cs +++ b/Source/Editor/Viewport/Previews/ModelPreview.cs @@ -409,8 +409,8 @@ namespace FlaxEditor.Viewport.Previews } var font = Style.Current.FontMedium; var pos = new Float2(10, 50); - Render2D.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); - Render2D.DrawText(font, text, new Rectangle(pos, Size), Color.White); + FallbackTextUtils.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); + FallbackTextUtils.DrawText(font, text, new Rectangle(pos, Size), Color.White); } } diff --git a/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs b/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs index c6ce5a7b5..a6eb721ce 100644 --- a/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs +++ b/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs @@ -254,7 +254,7 @@ namespace FlaxEditor.Viewport.Previews if (_showParticlesCounter) { var count = _previewEffect.ParticlesCount; - Render2D.DrawText( + FallbackTextUtils.DrawText( Style.Current.FontSmall, "Particles: " + count, new Rectangle(Float2.Zero, Size), diff --git a/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs b/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs index 8bcc506d9..6c02c019a 100644 --- a/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs +++ b/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs @@ -161,8 +161,8 @@ namespace FlaxEditor.Viewport.Previews } var font = Style.Current.FontMedium; var pos = new Float2(10, 50); - Render2D.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); - Render2D.DrawText(font, text, new Rectangle(pos, Size), Color.White); + FallbackTextUtils.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); + FallbackTextUtils.DrawText(font, text, new Rectangle(pos, Size), Color.White); } } diff --git a/Source/Editor/Viewport/Previews/TexturePreview.cs b/Source/Editor/Viewport/Previews/TexturePreview.cs index ec10bb019..5e7594e9c 100644 --- a/Source/Editor/Viewport/Previews/TexturePreview.cs +++ b/Source/Editor/Viewport/Previews/TexturePreview.cs @@ -103,7 +103,7 @@ namespace FlaxEditor.Viewport.Previews { var bounds = new Rectangle(Float2.Zero, Size); Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f)); - Render2D.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); } Render2D.PopClip(); diff --git a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs index 598f4ee32..43f990afb 100644 --- a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs +++ b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; using FlaxEditor.GUI.ContextMenu; using FlaxEngine; using FlaxEngine.GUI; @@ -124,7 +123,7 @@ namespace FlaxEditor.Viewport.Widgets } // Draw text - Render2D.DrawText(style.FontMedium, _text, textRect, style.ForegroundViewport * (IsMouseOver ? 1.0f : 0.9f), TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, _text, textRect, style.ForegroundViewport * (IsMouseOver ? 1.0f : 0.9f), TextAlignment.Center, TextAlignment.Center); } /// diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index 02aad16ec..8c38ed2a9 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -2,7 +2,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System.Collections.Generic; -using System.Linq; using FlaxEditor.GUI.Dialogs; using FlaxEngine; using FlaxEngine.GUI; diff --git a/Source/Editor/Windows/Assets/AnimationGraphWindow.cs b/Source/Editor/Windows/Assets/AnimationGraphWindow.cs index 12eac22b5..eb4198fd8 100644 --- a/Source/Editor/Windows/Assets/AnimationGraphWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationGraphWindow.cs @@ -51,7 +51,7 @@ namespace FlaxEditor.Windows.Assets var style = Style.Current; if (_window.Asset == null || !_window.Asset.IsLoaded) { - Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } } diff --git a/Source/Editor/Windows/Assets/AnimationWindow.cs b/Source/Editor/Windows/Assets/AnimationWindow.cs index 8f88d93f6..042cf2b37 100644 --- a/Source/Editor/Windows/Assets/AnimationWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationWindow.cs @@ -61,7 +61,7 @@ namespace FlaxEditor.Windows.Assets var animation = _window.Asset; if (animation == null || !animation.IsLoaded) { - Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Windows/Assets/ModelWindow.cs b/Source/Editor/Windows/Assets/ModelWindow.cs index c2764a5a6..d48966043 100644 --- a/Source/Editor/Windows/Assets/ModelWindow.cs +++ b/Source/Editor/Windows/Assets/ModelWindow.cs @@ -48,7 +48,7 @@ namespace FlaxEditor.Windows.Assets var asset = _window.Asset; if (asset == null || !asset.IsLoaded) { - Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } } @@ -645,7 +645,7 @@ namespace FlaxEditor.Windows.Assets if (!Proxy.Window._meshData.RequestMeshData(Proxy.Window._asset)) { Invalidate(); - Render2D.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); return; } diff --git a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs index 95827240c..1e7192d9f 100644 --- a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs +++ b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs @@ -50,7 +50,7 @@ namespace FlaxEditor.Windows.Assets var asset = _window.Asset; if (asset == null || !asset.IsLoaded) { - Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } } @@ -715,7 +715,7 @@ namespace FlaxEditor.Windows.Assets if (!Proxy.Window.RequestMeshData()) { Invalidate(); - Render2D.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); return; } diff --git a/Source/Editor/Windows/ContentWindow.Search.cs b/Source/Editor/Windows/ContentWindow.Search.cs index a1072d158..67b7deddd 100644 --- a/Source/Editor/Windows/ContentWindow.Search.cs +++ b/Source/Editor/Windows/ContentWindow.Search.cs @@ -57,7 +57,7 @@ namespace FlaxEditor.Windows var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + FallbackTextUtils.DrawText(Font.GetFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); // Arrow diff --git a/Source/Editor/Windows/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs index ab0c07ec5..91d4f9d8c 100644 --- a/Source/Editor/Windows/DebugLogWindow.cs +++ b/Source/Editor/Windows/DebugLogWindow.cs @@ -140,11 +140,11 @@ namespace FlaxEditor.Windows Render2D.PushClip(ref clientRect); if (LogCount == 1) { - Render2D.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground); + FallbackTextUtils.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground); } else if (LogCount > 1) { - Render2D.DrawText(style.FontMedium, $"{Desc.Title} ({LogCount})", textRect, style.Foreground); + FallbackTextUtils.DrawText(style.FontMedium, $"{Desc.Title} ({LogCount})", textRect, style.Foreground); } Render2D.PopClip(); } diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 4a8c11176..2c0364995 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -827,7 +827,7 @@ namespace FlaxEditor.Windows if (Camera.MainCamera == null) { var style = Style.Current; - Render2D.DrawText(style.FontLarge, "No camera", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontLarge, "No camera", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } // Selected UI controls outline @@ -866,8 +866,8 @@ namespace FlaxEditor.Windows var alpha = Mathf.Saturate(-animTime / fadeOutTime); var rect = new Rectangle(new Float2(6), Size - 12); var text = "Press Shift+F11 to unlock the mouse"; - Render2D.DrawText(style.FontSmall, text, rect + new Float2(1.0f), style.Background * alpha, TextAlignment.Near, TextAlignment.Far); - Render2D.DrawText(style.FontSmall, text, rect, style.Foreground * alpha, TextAlignment.Near, TextAlignment.Far); + FallbackTextUtils.DrawText(style.FontSmall, text, rect + new Float2(1.0f), style.Background * alpha, TextAlignment.Near, TextAlignment.Far); + FallbackTextUtils.DrawText(style.FontSmall, text, rect, style.Foreground * alpha, TextAlignment.Near, TextAlignment.Far); } timeout = 1.0f; @@ -884,7 +884,7 @@ namespace FlaxEditor.Windows { var bounds = new Rectangle(Float2.Zero, Size); Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f)); - Render2D.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); } } } diff --git a/Source/Editor/Windows/Profiler/SingleChart.cs b/Source/Editor/Windows/Profiler/SingleChart.cs index 4f36692e5..25241b4c6 100644 --- a/Source/Editor/Windows/Profiler/SingleChart.cs +++ b/Source/Editor/Windows/Profiler/SingleChart.cs @@ -138,8 +138,8 @@ namespace FlaxEditor.Windows.Profiler var headerRect = new Rectangle(0, chartHeight, Width, TitleHeight); var headerTextRect = new Rectangle(2, chartHeight, Width - 4, TitleHeight); Render2D.FillRectangle(headerRect, style.BackgroundNormal); - Render2D.DrawText(style.FontMedium, Title, headerTextRect, style.ForegroundGrey, TextAlignment.Near, TextAlignment.Center); - Render2D.DrawText(style.FontMedium, _sample, headerTextRect, style.Foreground, TextAlignment.Far, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, Title, headerTextRect, style.ForegroundGrey, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, _sample, headerTextRect, style.Foreground, TextAlignment.Far, TextAlignment.Center); } private void OnClick(ref Float2 location) diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index 780b31a3b..f87efdace 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -2,7 +2,6 @@ using FlaxEngine; using FlaxEngine.GUI; -using System.Linq; namespace FlaxEditor.Windows.Profiler { @@ -91,7 +90,7 @@ namespace FlaxEditor.Windows.Profiler if (_nameLength < bounds.Width + 4) { Render2D.PushClip(bounds); - Render2D.DrawText(style.FontMedium, _name, bounds, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(style.FontMedium, _name, bounds, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center); Render2D.PopClip(); } } @@ -116,7 +115,7 @@ namespace FlaxEditor.Windows.Profiler var style = Style.Current; var rect = new Rectangle(Float2.Zero, Size); Render2D.PushClip(rect); - Render2D.DrawText(style.FontMedium, Name, rect, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapChars); + FallbackTextUtils.DrawText(style.FontMedium, Name, rect, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapChars); Render2D.PopClip(); } } diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 63ba7b960..2fd990861 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -297,7 +297,7 @@ namespace FlaxEditor.Windows } if (overlayText != null) { - Render2D.DrawText(style.FontLarge, overlayText, GetClientArea(), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center, textWrap); + FallbackTextUtils.DrawText(style.FontLarge, overlayText, GetClientArea(), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center, textWrap); } base.Draw(); diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs index 16b9165e1..f86341df3 100644 --- a/Source/Editor/Windows/ToolboxWindow.cs +++ b/Source/Editor/Windows/ToolboxWindow.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEditor.GUI.Input; using FlaxEditor.GUI.Tabs; using FlaxEditor.GUI.Tree; @@ -273,8 +272,8 @@ namespace FlaxEditor.Windows var textRect = item.TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = font.GetCharPosition(text, ranges[i].StartIndex); - var end = font.GetCharPosition(text, ranges[i].EndIndex); + var start = FallbackTextUtils.GetCharPosition(font, text, ranges[i].StartIndex); + var end = FallbackTextUtils.GetCharPosition(font, text, ranges[i].EndIndex); highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } item.SetHighlights(highlights); diff --git a/Source/Engine/Render2D/FallbackTextUtils.cs b/Source/Engine/Render2D/FallbackTextUtils.cs index eb676f0a9..b6d8770e2 100644 --- a/Source/Engine/Render2D/FallbackTextUtils.cs +++ b/Source/Engine/Render2D/FallbackTextUtils.cs @@ -1,4 +1,6 @@ +using System.Runtime.CompilerServices; + namespace FlaxEngine { /// @@ -11,41 +13,10 @@ namespace FlaxEngine get; set; } = null; - public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void DrawText(Font font, string text, Color color, ref TextLayoutOptions layout, MaterialBase customMaterial = null, bool useFallback = true) { - var layout = new TextLayoutOptions - { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; - - if (Fallbacks != null) - { - Render2D.DrawText(font, Fallbacks, text, color, ref layout); - } - else - { - Render2D.DrawText(font, text, color, ref layout); - } - } - - public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f) - { - var layout = new TextLayoutOptions - { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; - - if (Fallbacks != null) + if (Fallbacks != null && useFallback) { Render2D.DrawText(font, Fallbacks, text, color, ref layout, customMaterial); } @@ -55,9 +26,56 @@ namespace FlaxEngine } } - public static Float2 MeasureText(Font font, string text) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) { - if (Fallbacks != null) + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + + if (Fallbacks != null && useFallback) + { + Render2D.DrawText(font, Fallbacks, text, color, ref layout); + } + else + { + Render2D.DrawText(font, text, color, ref layout); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) + { + var layout = new TextLayoutOptions + { + Bounds = layoutRect, + HorizontalAlignment = horizontalAlignment, + VerticalAlignment = verticalAlignment, + TextWrapping = textWrapping, + Scale = scale, + BaseLinesGapScale = baseLinesGapScale, + }; + + if (Fallbacks != null && useFallback) + { + Render2D.DrawText(font, Fallbacks, text, color, ref layout, customMaterial); + } + else + { + Render2D.DrawText(font, text, color, ref layout, customMaterial); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 MeasureText(Font font, string text, bool useFallback = true) + { + if (Fallbacks != null && useFallback) { return font.MeasureText(Fallbacks, text); } @@ -67,9 +85,10 @@ namespace FlaxEngine } } - public static Float2 MeasureText(Font font, string text, ref TextRange textRange) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 MeasureText(Font font, string text, ref TextRange textRange, bool useFallback = true) { - if (Fallbacks != null) + if (Fallbacks != null && useFallback) { return font.MeasureText(Fallbacks, text, ref textRange); } @@ -79,9 +98,10 @@ namespace FlaxEngine } } - public static Float2 MeasureText(Font font, string text, ref TextLayoutOptions layout) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 MeasureText(Font font, string text, ref TextLayoutOptions layout, bool useFallback = true) { - if (Fallbacks != null) + if (Fallbacks != null && useFallback) { return font.MeasureText(Fallbacks, text, ref layout); } @@ -91,9 +111,10 @@ namespace FlaxEngine } } - public static Float2 MeasureText(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 MeasureText(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout, bool useFallback = true) { - if (Fallbacks != null) + if (Fallbacks != null && useFallback) { return font.MeasureText(Fallbacks, text, ref textRange, ref layout); } @@ -103,24 +124,108 @@ namespace FlaxEngine } } - public static Float2 GetCharPosition(Font font, string text, int index) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int HitTestText(Font font, string text, Float2 location, bool useFallback = true) { - return font.GetCharPosition(Style.Current.Fallbacks, text, index); + if (Fallbacks != null && useFallback) + { + return font.HitTestText(Fallbacks, text, location); + } + else + { + return font.HitTestText(text, location); + } } - public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int HitTestText(Font font, string text, ref TextRange textRange, Float2 location, bool useFallback = true) { - return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index); + if (Fallbacks != null && useFallback) + { + return font.HitTestText(Fallbacks, text, ref textRange, location); + } + else + { + return font.HitTestText(text, ref textRange, location); + } } - public static Float2 GetCharPosition(Font font, string text, int index, ref TextLayoutOptions layout) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int HitTestText(Font font, string text, Float2 location, ref TextLayoutOptions layout, bool useFallback = true) { - return font.GetCharPosition(Style.Current.Fallbacks, text, index, ref layout); + if (Fallbacks != null && useFallback) + { + return font.HitTestText(Fallbacks, text, location, ref layout); + } + else + { + return font.HitTestText(text, location, ref layout); + } } - public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index, ref TextLayoutOptions layout) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int HitTestText(Font font, string text, ref TextRange textRange, Float2 location, ref TextLayoutOptions layout, bool useFallback = true) { - return font.GetCharPosition(Style.Current.Fallbacks, text, ref textRange, index, ref layout); + if (Fallbacks != null && useFallback) + { + return font.HitTestText(Fallbacks, text, ref textRange, location, ref layout); + } + else + { + return font.HitTestText(text, ref textRange, location, ref layout); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 GetCharPosition(Font font, string text, int index, bool useFallback = true) + { + if (Fallbacks != null && useFallback) + { + return font.GetCharPosition(Fallbacks, text, index); + } + else + { + return font.GetCharPosition(text, index); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index, bool useFallback = true) + { + if (Fallbacks != null && useFallback) + { + return font.GetCharPosition(Fallbacks, text, ref textRange, index); + } + else + { + return font.GetCharPosition(text, ref textRange, index); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 GetCharPosition(Font font, string text, int index, ref TextLayoutOptions layout, bool useFallback = true) + { + if (Fallbacks != null && useFallback) + { + return font.GetCharPosition(Fallbacks, text, index, ref layout); + } + else + { + return font.GetCharPosition(text, index, ref layout); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index, ref TextLayoutOptions layout, bool useFallback = true) + { + if (Fallbacks != null && useFallback) + { + return font.GetCharPosition(Fallbacks, text, ref textRange, index, ref layout); + } + else + { + return font.GetCharPosition(text, ref textRange, index, ref layout); + } } } } diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index 756501c5e..feb34418b 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; namespace FlaxEngine.GUI { @@ -262,7 +261,7 @@ namespace FlaxEngine.GUI Render2D.DrawRectangle(clientRect, borderColor, BorderThickness); // Draw text - Render2D.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); + FallbackTextUtils.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); } /// diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 46f05f516..3b298b136 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; namespace FlaxEngine.GUI { @@ -674,7 +673,7 @@ namespace FlaxEngine.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - Render2D.DrawText(Font.GetFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(Font.GetFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); } diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index 4e26c1934..3d1caad28 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -1,8 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using FlaxEditor.Options; using System.ComponentModel; -using System.Linq; namespace FlaxEngine.GUI { @@ -235,7 +233,7 @@ namespace FlaxEngine.GUI } } - Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + FallbackTextUtils.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); if (ClipText) Render2D.PopClip(); diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs index d3c2feefe..df8e0be7c 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using FlaxEngine.Utilities; -using System.Linq; namespace FlaxEngine.GUI { diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.cs b/Source/Engine/UI/GUI/Common/RichTextBox.cs index 514878b37..ab922d93d 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System.Collections.Generic; -using System.Linq; namespace FlaxEngine.GUI { diff --git a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs index 438a7e3d8..a30d8e603 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs @@ -154,7 +154,7 @@ namespace FlaxEngine.GUI if (!font) break; height = font.Height / DpiScale; - return textBlock.Bounds.Location + font.GetCharPosition(_text, ref textBlock.Range, index - textBlock.Range.StartIndex); + return textBlock.Bounds.Location + FallbackTextUtils.GetCharPosition(font, _text, ref textBlock.Range, index - textBlock.Range.StartIndex); } } @@ -196,7 +196,7 @@ namespace FlaxEngine.GUI var font = textBlock.Style.Font.GetFont(); if (!font && textBlock.Range.Length > 0) break; - return font.HitTestText(_text, ref textBlock.Range, location - textBlock.Bounds.Location) + textBlock.Range.StartIndex; + return FallbackTextUtils.HitTestText(font, _text, ref textBlock.Range, location - textBlock.Bounds.Location) + textBlock.Range.StartIndex; } } @@ -288,8 +288,8 @@ namespace FlaxEngine.GUI // Selection if (hasSelection && textBlock.Style.BackgroundSelectedBrush != null && textBlock.Range.Intersect(ref selection)) { - var leftEdge = selection.StartIndex <= textBlock.Range.StartIndex ? textBlock.Bounds.UpperLeft : font.GetCharPosition(_text, selection.StartIndex); - var rightEdge = selection.EndIndex >= textBlock.Range.EndIndex ? textBlock.Bounds.UpperRight : font.GetCharPosition(_text, selection.EndIndex); + var leftEdge = selection.StartIndex <= textBlock.Range.StartIndex ? textBlock.Bounds.UpperLeft : FallbackTextUtils.GetCharPosition(font, _text, selection.StartIndex); + var rightEdge = selection.EndIndex >= textBlock.Range.EndIndex ? textBlock.Bounds.UpperRight : FallbackTextUtils.GetCharPosition(font, _text, selection.EndIndex); float height = font.Height / DpiScale; float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); alpha *= alpha; diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index ea7523f48..70a153faf 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -1,7 +1,5 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using System.Drawing; -using System.Linq; namespace FlaxEngine.GUI { @@ -120,7 +118,7 @@ namespace FlaxEngine.GUI } height = font.Height / DpiScale; - return font.GetCharPosition(_text, index, ref _layout); + return FallbackTextUtils.GetCharPosition(font, _text, index, ref _layout); } /// @@ -132,7 +130,7 @@ namespace FlaxEngine.GUI return 0; } - return font.HitTestText(_text, location, ref _layout); + return FallbackTextUtils.HitTestText(font, _text, location, ref _layout); } /// @@ -171,8 +169,8 @@ namespace FlaxEngine.GUI // Check if sth is selected to draw selection if (HasSelection) { - var leftEdge = font.GetCharPosition(_text, SelectionLeft, ref _layout); - var rightEdge = font.GetCharPosition(_text, SelectionRight, ref _layout); + var leftEdge = FallbackTextUtils.GetCharPosition(font, _text, SelectionLeft, ref _layout); + var rightEdge = FallbackTextUtils.GetCharPosition(font, _text, SelectionRight, ref _layout); float fontHeight = font.Height / DpiScale; // Draw selection background @@ -213,25 +211,11 @@ namespace FlaxEngine.GUI var color = TextColor; if (!enabled) color *= 0.6f; - if (Render2D.Fallbacks != null) - { - Render2D.DrawText(font, Render2D.Fallbacks, _text, color, ref _layout, TextMaterial); - } - else - { - Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); - } + FallbackTextUtils.DrawText(font, _text, color, ref _layout, TextMaterial); } else if (!string.IsNullOrEmpty(_watermarkText) && !IsFocused) { - if (Render2D.Fallbacks != null) - { - Render2D.DrawText(font, Render2D.Fallbacks, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); - } - else - { - Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); - } + FallbackTextUtils.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); } // Caret diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index ed620ab98..123e0f034 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; namespace FlaxEngine.GUI { @@ -375,7 +374,7 @@ namespace FlaxEngine.GUI textColor *= 0.6f; } - Render2D.DrawText(HeaderTextFont.GetFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + FallbackTextUtils.DrawText(HeaderTextFont.GetFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); if (!_isClosed && EnableContainmentLines) { diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index c4b51b085..f4fca13eb 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -1,6 +1,5 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using System.Linq; namespace FlaxEngine.GUI { diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index 7a1026e55..41d06b017 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Linq; namespace FlaxEngine.GUI { @@ -235,7 +234,7 @@ namespace FlaxEngine.GUI Render2D.FillRectangle(new Rectangle(1.1f, 1.1f, Width - 2, Height - 2), style.Background); // Tooltip text - Render2D.DrawText( + FallbackTextUtils.DrawText( style.FontMedium, _currentText, GetClientArea(), From 3c5035d3e93540fa718ca97accd5ce94e7afa7a4 Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Fri, 1 Dec 2023 18:06:25 +0800 Subject: [PATCH 09/16] Fix merge problem --- Source/Editor/Windows/PluginsWindow.cs | 55 +++++++++++++++++++++----- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index 03beab2fa..f43fb0342 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -14,7 +14,6 @@ using FlaxEditor.GUI.Tabs; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Json; -using FlaxEngine.Utilities; namespace FlaxEditor.Windows { @@ -122,7 +121,7 @@ namespace FlaxEditor.Windows url = desc.RepositoryUrl; versionLabel.Font.Font.WaitForLoaded(); var font = versionLabel.Font.GetFont(); - var authorWidth = FallbackTextUtils.MeasureText(font, desc.Author).X + 8; + var authorWidth = font.MeasureText(desc.Author).X + 8; var authorLabel = new ClickableLabel { HorizontalAlignment = TextAlignment.Far, @@ -392,6 +391,7 @@ namespace FlaxEditor.Windows } Editor.Log("Plugin project has been cloned."); + try { // Start git submodule clone @@ -412,24 +412,28 @@ namespace FlaxEditor.Windows } // Find project config file. Could be different then what the user named the folder. - var files = Directory.GetFiles(clonePath); string pluginProjectName = ""; - foreach (var file in files) + foreach (var file in Directory.GetFiles(clonePath)) { if (file.Contains(".flaxproj", StringComparison.OrdinalIgnoreCase)) { pluginProjectName = Path.GetFileNameWithoutExtension(file); - Debug.Log(pluginProjectName); + break; } } - if (string.IsNullOrEmpty(pluginProjectName)) - Editor.LogError("Failed to find plugin project file to add to Project config. Please add manually."); - else { - await AddReferenceToProject(pluginName, pluginProjectName); - MessageBox.Show($"{pluginName} has been successfully cloned. Restart editor for changes to take effect.", "Plugin Project Created", MessageBoxButtons.OK); + Editor.LogError("Failed to find plugin project file to add to Project config. Please add manually."); + return; } + + await AddModuleReferencesInGameModule(clonePath); + await AddReferenceToProject(pluginName, pluginProjectName); + + if (Editor.Options.Options.SourceCode.AutoGenerateScriptsProjectFiles) + Editor.ProgressReporting.GenerateScriptsProjectFiles.RunAsync(); + + MessageBox.Show($"{pluginName} has been successfully cloned. Restart editor for changes to take effect.", "Plugin Project Created", MessageBoxButtons.OK); } private void OnAddButtonClicked() @@ -749,6 +753,37 @@ namespace FlaxEditor.Windows MessageBox.Show($"{pluginName} has been successfully created. Restart editor for changes to take effect.", "Plugin Project Created", MessageBoxButtons.OK); } + private async Task AddModuleReferencesInGameModule(string pluginFolderPath) + { + // Common game build script location + var gameScript = Path.Combine(Globals.ProjectFolder, "Source/Game/Game.Build.cs"); + if (File.Exists(gameScript)) + { + var gameScriptContents = await File.ReadAllTextAsync(gameScript); + var insertLocation = gameScriptContents.IndexOf("base.Setup(options);", StringComparison.Ordinal); + if (insertLocation != -1) + { + insertLocation += 20; + var modifiedAny = false; + + // Find all code modules in a plugin to auto-reference them in game build script + foreach (var subDir in Directory.GetDirectories(Path.Combine(pluginFolderPath, "Source"))) + { + var pluginModuleName = Path.GetFileName(subDir); + var pluginModuleScriptPath = Path.Combine(subDir, pluginModuleName + ".Build.cs"); + if (File.Exists(pluginModuleScriptPath)) + { + gameScriptContents = gameScriptContents.Insert(insertLocation, $"\n options.PublicDependencies.Add(\"{pluginModuleName}\");"); + modifiedAny = true; + } + } + + if (modifiedAny) + await File.WriteAllTextAsync(gameScript, gameScriptContents, Encoding.UTF8); + } + } + } + private async Task AddReferenceToProject(string pluginFolderName, string pluginName) { // Project flax config file From 95f5e31e4852ab6e2a25177f3f587ba0840b03a4 Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Fri, 1 Dec 2023 19:38:15 +0800 Subject: [PATCH 10/16] Fix textbox height Fix build error under non-windows platforms --- Source/Editor/GUI/Row.cs | 5 ++- Source/Engine/Render2D/FallbackFonts.h | 35 +++++++++++++++--- Source/Engine/Render2D/FallbackTextUtils.cs | 37 +++++++++++++++++++ Source/Engine/Render2D/Font.h | 7 ++-- Source/Engine/Render2D/FontAsset.cpp | 7 ++++ Source/Engine/Render2D/FontAsset.h | 2 +- .../Engine/UI/GUI/Common/RichTextBoxBase.cs | 1 + Source/Engine/UI/GUI/Common/TextBox.cs | 4 +- 8 files changed, 85 insertions(+), 13 deletions(-) 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); From 29eb3954c5bc0da8c7ce0b2e997cc797c2b1754c Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:48:51 +0800 Subject: [PATCH 11/16] Create global settings for font fallback --- Source/Editor/Windows/SplashScreen.cpp | 6 +- Source/Engine/Core/Config/GameSettings.cs | 2 +- Source/Engine/Core/Config/GameSettings.h | 11 +++ .../Platform/Win32/IncludeWindowsHeaders.h | 2 +- Source/Engine/Render2D/FallbackFonts.cpp | 2 +- Source/Engine/Render2D/FallbackFonts.h | 14 ++-- Source/Engine/Render2D/Font.cpp | 8 +- Source/Engine/Render2D/Font.h | 36 ++++----- Source/Engine/Render2D/Render2D.cpp | 24 +++--- Source/Engine/Render2D/Render2D.h | 81 +++++++++++++++---- 10 files changed, 122 insertions(+), 64 deletions(-) diff --git a/Source/Editor/Windows/SplashScreen.cpp b/Source/Editor/Windows/SplashScreen.cpp index 92894b537..cbb51ae3c 100644 --- a/Source/Editor/Windows/SplashScreen.cpp +++ b/Source/Editor/Windows/SplashScreen.cpp @@ -264,7 +264,7 @@ void SplashScreen::OnDraw() layout.HorizontalAlignment = TextAlignment::Near; layout.VerticalAlignment = TextAlignment::Near; layout.Scale = Math::Min((width - 20 * s) / titleLength.X, 1.0f); - Render2D::DrawText(_titleFont, GetTitle(), Color::White, layout); + Render2D::DrawTextInternal(_titleFont, GetTitle(), Color::White, layout); // Subtitle String subtitle(_quote); @@ -279,14 +279,14 @@ void SplashScreen::OnDraw() layout.Scale = 1.0f; layout.HorizontalAlignment = TextAlignment::Far; layout.VerticalAlignment = TextAlignment::Far; - Render2D::DrawText(_subtitleFont, subtitle, Color::FromRGB(0x8C8C8C), layout); + Render2D::DrawTextInternal(_subtitleFont, subtitle, Color::FromRGB(0x8C8C8C), layout); // Additional info const float infoMargin = 6 * s; layout.Bounds = Rectangle(infoMargin, lightBarHeight + infoMargin, width - (2 * infoMargin), height - lightBarHeight - (2 * infoMargin)); layout.HorizontalAlignment = TextAlignment::Near; layout.VerticalAlignment = TextAlignment::Center; - Render2D::DrawText(_subtitleFont, _infoText, Color::FromRGB(0xFFFFFF) * 0.9f, layout); + Render2D::DrawTextInternal(_subtitleFont, _infoText, Color::FromRGB(0xFFFFFF) * 0.9f, layout); } bool SplashScreen::HasLoadedFonts() const diff --git a/Source/Engine/Core/Config/GameSettings.cs b/Source/Engine/Core/Config/GameSettings.cs index 40d9f5dbc..76c968968 100644 --- a/Source/Engine/Core/Config/GameSettings.cs +++ b/Source/Engine/Core/Config/GameSettings.cs @@ -119,7 +119,7 @@ namespace FlaxEditor.Content.Settings /// /// The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair). /// - [EditorOrder(1100), EditorDisplay("Other Settings"), Tooltip("The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).")] + [EditorOrder(1500), EditorDisplay("Other Settings"), Tooltip("The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).")] public Dictionary CustomSettings; #if FLAX_EDITOR || PLATFORM_WINDOWS diff --git a/Source/Engine/Core/Config/GameSettings.h b/Source/Engine/Core/Config/GameSettings.h index 54ad29a7b..6813f3ee2 100644 --- a/Source/Engine/Core/Config/GameSettings.h +++ b/Source/Engine/Core/Config/GameSettings.h @@ -7,6 +7,8 @@ #include "Engine/Core/Types/String.h" #include "Engine/Core/Collections/Dictionary.h" +class FontFallbackList; + /// /// The main game engine configuration service. Loads and applies game configuration. /// @@ -33,6 +35,15 @@ public: API_FIELD(Attributes="EditorOrder(15), EditorDisplay(\"General\")") String CopyrightNotice; + /// + /// The copyright note used for content signing (eg. source code header). + /// + API_FIELD(Attributes = "EditorOrder(1200), EditorDisplay(\"Other Settings\")") + bool EnableFontFallback; + + API_FIELD(Attributes = "EditorOrder(1205), EditorDisplay(\"Other Settings\")") + FontFallbackList* FontFallbacks; + /// /// The default application icon. /// diff --git a/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h b/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h index 3f2b8d36e..66ed97a20 100644 --- a/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h +++ b/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h @@ -57,7 +57,7 @@ #undef CreateWindow #undef CreateProcess #undef SetWindowText -#undef DrawText +#undef DrawTextInternal #undef CreateFont #undef IsMinimized #undef IsMaximized diff --git a/Source/Engine/Render2D/FallbackFonts.cpp b/Source/Engine/Render2D/FallbackFonts.cpp index b79a0db4e..7f81a42bb 100644 --- a/Source/Engine/Render2D/FallbackFonts.cpp +++ b/Source/Engine/Render2D/FallbackFonts.cpp @@ -2,7 +2,7 @@ #include "FontManager.h" #include "Engine/Core/Math/Math.h" -FallbackFonts::FallbackFonts(const Array& fonts) +FontFallbackList::FontFallbackList(const Array& fonts) : ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer)), _fontAssets(fonts) { diff --git a/Source/Engine/Render2D/FallbackFonts.h b/Source/Engine/Render2D/FallbackFonts.h index 040ca8087..fb247e4e2 100644 --- a/Source/Engine/Render2D/FallbackFonts.h +++ b/Source/Engine/Render2D/FallbackFonts.h @@ -12,9 +12,9 @@ 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 +API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API FontFallbackList : public ManagedScriptingObject { - DECLARE_SCRIPTING_TYPE_NO_SPAWN(FallbackFonts); + DECLARE_SCRIPTING_TYPE_NO_SPAWN(FontFallbackList); private: Array _fontAssets; @@ -23,18 +23,18 @@ private: public: /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The fallback font assets. - FallbackFonts(const Array& fonts); + FontFallbackList(const Array& fonts); /// - /// Initializes a new instance of the class, exposed for C#. + /// 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); + API_FUNCTION() FORCE_INLINE static FontFallbackList* Create(const Array& fonts) { + return New(fonts); } /// diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index 4cf1f73a2..5d029ca5e 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -293,7 +293,7 @@ void Font::ProcessText(const StringView& text, Array& outputLines } } -void Font::ProcessText(FallbackFonts* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) +void Font::ProcessText(FontFallbackList* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout) { const Array& fallbackFonts = fallbacks->GetFontList(GetSize()); float cursorX = 0; @@ -569,7 +569,7 @@ Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout return max; } -Float2 Font::MeasureText(FallbackFonts* fallbacks, const StringView& text, const TextLayoutOptions& layout) +Float2 Font::MeasureText(FontFallbackList* fallbacks, const StringView& text, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) @@ -664,7 +664,7 @@ int32 Font::HitTestText(const StringView& text, const Float2& location, const Te return smallestIndex; } -int32 Font::HitTestText(FallbackFonts* fallbacks, const StringView& text, const Float2& location, const TextLayoutOptions& layout) +int32 Font::HitTestText(FontFallbackList* fallbacks, const StringView& text, const Float2& location, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.Length() <= 0) @@ -818,7 +818,7 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); } -Float2 Font::GetCharPosition(FallbackFonts* fallbacks, const StringView& text, int32 index, const TextLayoutOptions& layout) +Float2 Font::GetCharPosition(FontFallbackList* fallbacks, const StringView& text, int32 index, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index 110a8fe70..6bb2849e2 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -10,7 +10,7 @@ #include "TextLayoutOptions.h" class FontAsset; -class FallbackFonts; +class FontFallbackList; struct FontTextureAtlasSlot; struct BlockedTextLineCache; @@ -463,7 +463,7 @@ public: /// The input text. /// The layout properties. /// The output lines list. - void ProcessText(FallbackFonts* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); + void ProcessText(FontFallbackList* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); /// /// Processes text to get cached lines for rendering. @@ -471,7 +471,7 @@ public: /// The input text. /// The layout properties. /// The output lines list. - API_FUNCTION() Array ProcessText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(fallbacks, text, lines, layout); @@ -485,7 +485,7 @@ public: /// The input text range (substring range of the input text parameter). /// The layout properties. /// The output lines list. - API_FUNCTION() Array ProcessText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(fallbacks, textRange.Substring(text), lines, layout); @@ -497,7 +497,7 @@ public: /// /// The input text. /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(FallbackFonts* fallbacks, const StringView& text) + API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text) { return ProcessText(fallbacks, text, TextLayoutOptions()); } @@ -508,7 +508,7 @@ public: /// The input text. /// The input text range (substring range of the input text parameter). /// The output lines list. - API_FUNCTION() FORCE_INLINE Array ProcessText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) + API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) { return ProcessText(fallbacks, textRange.Substring(text), TextLayoutOptions()); } @@ -560,7 +560,7 @@ public: /// The input text to test. /// The layout properties. /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); /// /// Measures minimum size of the rectangle that will be needed to draw given text. @@ -569,7 +569,7 @@ public: /// The input text range (substring range of the input text parameter). /// The layout properties. /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) { return MeasureText(fallbacks, textRange.Substring(text), layout); } @@ -579,7 +579,7 @@ public: /// . /// The input text to test. /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text) + API_FUNCTION() FORCE_INLINE Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text) { return MeasureText(fallbacks, text, TextLayoutOptions()); } @@ -590,7 +590,7 @@ public: /// The input text to test. /// The input text range (substring range of the input text parameter). /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) + API_FUNCTION() FORCE_INLINE Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) { return MeasureText(fallbacks, textRange.Substring(text), TextLayoutOptions()); } @@ -647,7 +647,7 @@ public: /// The input location to test. /// The text layout properties. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); /// /// Calculates hit character index at given location. @@ -657,7 +657,7 @@ public: /// The input location to test. /// The text layout properties. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) { return HitTestText(fallbacks, textRange.Substring(text), location, layout); } @@ -668,7 +668,7 @@ public: /// The input text to test. /// The input location to test. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, const Float2& location) + API_FUNCTION() FORCE_INLINE int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, const Float2& location) { return HitTestText(fallbacks, text, location, TextLayoutOptions()); } @@ -680,7 +680,7 @@ public: /// The input text range (substring range of the input text parameter). /// The input location to test. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) + API_FUNCTION() FORCE_INLINE int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) { return HitTestText(fallbacks, textRange.Substring(text), location, TextLayoutOptions()); } @@ -737,7 +737,7 @@ public: /// The text position to get coordinates of. /// The text layout properties. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); /// /// Calculates character position for given text and character index. @@ -747,7 +747,7 @@ public: /// The text position to get coordinates of. /// The text layout properties. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) { return GetCharPosition(fallbacks, textRange.Substring(text), index, layout); } @@ -758,7 +758,7 @@ public: /// The input text to test. /// The text position to get coordinates of. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, int32 index) + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, int32 index) { return GetCharPosition(fallbacks, text, index, TextLayoutOptions()); } @@ -770,7 +770,7 @@ public: /// The input text range (substring range of the input text parameter). /// The text position to get coordinates of. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) { return GetCharPosition(fallbacks, textRange.Substring(text), index, TextLayoutOptions()); } diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 4e0cacb44..351bb3700 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -1144,7 +1144,7 @@ void DrawBatch(int32 startIndex, int32 count) Context->DrawIndexed(countIb, 0, d.StartIB); } -void Render2D::DrawText(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; @@ -1252,12 +1252,12 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, } } -void Render2D::DrawText(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) { - DrawText(font, textRange.Substring(text), color, location, customMaterial); + DrawTextInternal(font, textRange.Substring(text), color, location, customMaterial); } -void Render2D::DrawText(Font* font, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; @@ -1365,12 +1365,12 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color, } } -void Render2D::DrawText(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { - DrawText(font, textRange.Substring(text), color, layout, customMaterial); + DrawTextInternal(font, textRange.Substring(text), color, layout, customMaterial); } -void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; @@ -1549,12 +1549,12 @@ void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& } } -void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial) { - DrawText(font, fallbacks, textRange.Substring(text), color, location, customMaterial); + DrawTextInternal(font, fallbacks, textRange.Substring(text), color, location, customMaterial); } -void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { RENDER2D_CHECK_RENDERING_STATE; @@ -1673,9 +1673,9 @@ void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& } } -void Render2D::DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) +void Render2D::DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial) { - DrawText(font, fallbacks, textRange.Substring(text), color, layout, customMaterial); + DrawTextInternal(font, fallbacks, textRange.Substring(text), color, layout, customMaterial); } FORCE_INLINE bool NeedAlphaWithTint(const Color& color) diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index c99d38104..fa59b1d79 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -15,7 +15,7 @@ struct Matrix3x3; struct Viewport; struct TextRange; class Font; -class FallbackFonts; +class FontFallbackList; class GPUPipelineState; class GPUTexture; class GPUTextureView; @@ -54,6 +54,9 @@ API_CLASS(Static) class FLAXENGINE_API Render2D }; public: + API_FIELD() static bool EnableFontFallback; + API_FIELD() static FontFallbackList* FallbackFonts; + /// /// Checks if interface is during rendering phrase (Draw calls may be performed without failing). /// @@ -175,17 +178,17 @@ public: public: /// - /// Draws a text. + /// Draws a text, with font fallbacking disabled. /// /// The font to use. /// The text to render. /// The text color. /// The text location. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// - /// Draws a text. + /// Draws a text, with font fallbacking disabled. /// /// The font to use. /// The text to render. @@ -193,20 +196,20 @@ public: /// The text color. /// The text location. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// - /// Draws a text with formatting. + /// Draws a text with formatting, with font fallbacking disabled. /// /// The font to use. /// The text to render. /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// - /// Draws a text with formatting. + /// Draws a text with formatting, with font fallbacking disabled. /// /// The font to use. /// The text to render. @@ -214,10 +217,10 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// - /// Draws a text. + /// Draws a text, using custom fallback options. /// /// The fonts to use, ordered by priority. /// The text to render. @@ -225,20 +228,20 @@ public: /// The text color. /// The text location. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// - /// Draws a text with formatting. + /// Draws a text with formatting, using custom fallback options. /// /// The fonts to use, ordered by priority. /// The text to render. /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr); /// - /// Draws a text with formatting. + /// Draws a text with formatting, using custom fallback options. /// /// The fonts to use, ordered by priority. /// The text to render. @@ -246,10 +249,10 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); /// - /// Draws a text with formatting. + /// Draws a text with formatting, using custom fallback options. /// /// The fonts to use, ordered by priority. /// The text to render. @@ -257,7 +260,51 @@ public: /// The text color. /// The text layout properties. /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. - API_FUNCTION() static void DrawText(Font* font, FallbackFonts* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + API_FUNCTION() static void DrawTextInternal(Font* font, FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr); + + /// + /// Draws a text, follows the fallback settings defined in . + /// + /// The font to use. + /// The text to render. + /// The text color. + /// The text location. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. + API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr) { + if (EnableFontFallback && FallbackFonts) { + DrawTextInternal(font, FallbackFonts, text, color, location, customMaterial); + } + else { + DrawTextInternal(font, text, color, location, customMaterial); + } + } + + API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr) { + if (EnableFontFallback && FallbackFonts) { + DrawTextInternal(font, FallbackFonts, text, textRange, color, location, customMaterial); + } + else { + DrawTextInternal(font, text, textRange, color, location, customMaterial); + } + } + + API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr) { + if (EnableFontFallback && FallbackFonts) { + DrawTextInternal(font, FallbackFonts, text, color, layout, customMaterial); + } + else { + DrawTextInternal(font, text, color, layout, customMaterial); + } + } + + API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr) { + if (EnableFontFallback && FallbackFonts) { + DrawTextInternal(font, FallbackFonts, text, textRange, color, layout, customMaterial); + } + else { + DrawTextInternal(font, text, textRange, color, layout, customMaterial); + } + } /// /// Fills a rectangle area. From 2f019d4264fc7dfd57777297a8b592ab4064bf86 Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:51:32 +0800 Subject: [PATCH 12/16] Fix unintended unname --- Source/Engine/Platform/Win32/IncludeWindowsHeaders.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h b/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h index 66ed97a20..3f2b8d36e 100644 --- a/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h +++ b/Source/Engine/Platform/Win32/IncludeWindowsHeaders.h @@ -57,7 +57,7 @@ #undef CreateWindow #undef CreateProcess #undef SetWindowText -#undef DrawTextInternal +#undef DrawText #undef CreateFont #undef IsMinimized #undef IsMaximized From 23b71e7d3e825a3cb7d44300aa4e65c4fb684405 Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Sun, 3 Dec 2023 15:11:47 +0800 Subject: [PATCH 13/16] Make font fallbacking the default option --- Source/Editor/Content/GUI/ContentView.cs | 2 +- Source/Editor/Content/Items/ContentItem.cs | 2 +- Source/Editor/Content/Tree/ContentTreeNode.cs | 4 +- .../Dedicated/MeshReferenceEditor.cs | 4 +- .../CustomEditors/Dedicated/ScriptsEditor.cs | 4 +- .../CustomEditors/Dedicated/SplineEditor.cs | 2 +- .../Dedicated/UIControlEditor.cs | 2 +- .../Editors/ActorTransformEditor.cs | 2 +- .../Editors/FlaxObjectRefEditor.cs | 4 +- .../Editor/CustomEditors/Editors/TagEditor.cs | 2 +- .../CustomEditors/Editors/TypeEditor.cs | 4 +- Source/Editor/GUI/AssetPicker.cs | 6 +- Source/Editor/GUI/ComboBox.cs | 2 +- .../GUI/ContextMenu/ContextMenuButton.cs | 8 +- Source/Editor/GUI/CurveEditor.cs | 2 +- .../Editor/GUI/Dialogs/ColorPickerDialog.cs | 22 +- Source/Editor/GUI/Docking/DockPanelProxy.cs | 4 +- Source/Editor/GUI/Docking/DockWindow.cs | 2 +- Source/Editor/GUI/ItemsListContextMenu.cs | 6 +- Source/Editor/GUI/MainMenuButton.cs | 4 +- Source/Editor/GUI/NavigationButton.cs | 4 +- Source/Editor/GUI/Row.cs | 4 +- Source/Editor/GUI/StatusBar.cs | 2 +- Source/Editor/GUI/StyleValueEditor.cs | 2 +- Source/Editor/GUI/Table.cs | 2 +- Source/Editor/GUI/Tabs/Tabs.cs | 2 +- Source/Editor/GUI/Timeline/GUI/Background.cs | 2 +- Source/Editor/GUI/Timeline/Track.cs | 2 +- .../Editor/GUI/Timeline/Tracks/MemberTrack.cs | 2 +- Source/Editor/GUI/ToolStripButton.cs | 4 +- Source/Editor/GUI/Tree/TreeNode.cs | 4 +- Source/Editor/Options/OptionsModule.cs | 7 +- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 4 +- .../Archetypes/Animation.StateMachine.cs | 8 +- Source/Editor/Surface/Archetypes/Animation.cs | 2 +- .../Editor/Surface/Archetypes/BehaviorTree.cs | 6 +- .../Surface/Archetypes/ParticleModules.cs | 2 +- Source/Editor/Surface/Archetypes/Particles.cs | 4 +- .../Surface/ContextMenu/VisjectCMItem.cs | 20 +- Source/Editor/Surface/Elements/InputBox.cs | 6 +- Source/Editor/Surface/Elements/OutputBox.cs | 2 +- Source/Editor/Surface/Elements/TextView.cs | 2 +- Source/Editor/Surface/SurfaceComment.cs | 2 +- Source/Editor/Surface/SurfaceNode.cs | 8 +- Source/Editor/Tools/Foliage/FoliageTab.cs | 2 +- Source/Editor/Tools/Terrain/CarveTab.cs | 2 +- Source/Editor/Viewport/EditorViewport.cs | 12 +- .../Viewport/Previews/AnimationPreview.cs | 4 +- .../Editor/Viewport/Previews/ModelPreview.cs | 4 +- .../Previews/ParticleSystemPreview.cs | 2 +- .../Viewport/Previews/SkinnedModelPreview.cs | 4 +- .../Viewport/Previews/TexturePreview.cs | 2 +- .../Viewport/Widgets/ViewportWidgetButton.cs | 4 +- Source/Editor/Windows/AboutDialog.cs | 2 +- .../Windows/Assets/AnimationGraphWindow.cs | 2 +- .../Editor/Windows/Assets/AnimationWindow.cs | 2 +- Source/Editor/Windows/Assets/ModelWindow.cs | 4 +- .../Windows/Assets/SkinnedModelWindow.cs | 4 +- Source/Editor/Windows/ContentWindow.Search.cs | 2 +- Source/Editor/Windows/DebugLogWindow.cs | 4 +- Source/Editor/Windows/GameWindow.cs | 8 +- Source/Editor/Windows/Profiler/SingleChart.cs | 4 +- Source/Editor/Windows/Profiler/Timeline.cs | 6 +- Source/Editor/Windows/SceneTreeWindow.cs | 2 +- Source/Editor/Windows/SplashScreen.cpp | 2 +- Source/Editor/Windows/ToolboxWindow.cs | 4 +- Source/Engine/Core/Config/GameSettings.h | 9 - Source/Engine/Core/Config/GraphicsSettings.h | 14 + Source/Engine/Graphics/Graphics.cpp | 4 + Source/Engine/Render2D/FallbackTextUtils.cs | 268 --------- Source/Engine/Render2D/Font.cpp | 24 +- Source/Engine/Render2D/Font.h | 567 ++++++++++++------ Source/Engine/Render2D/Render2D.cpp | 2 + Source/Engine/Render2D/Render2D.cs | 4 +- Source/Engine/Render2D/Render2D.h | 6 +- Source/Engine/UI/GUI/Common/Button.cs | 2 +- Source/Engine/UI/GUI/Common/Dropdown.cs | 4 +- Source/Engine/UI/GUI/Common/Label.cs | 4 +- .../Engine/UI/GUI/Common/RichTextBoxBase.cs | 8 +- Source/Engine/UI/GUI/Common/TextBox.cs | 18 +- Source/Engine/UI/GUI/Panels/DropPanel.cs | 2 +- Source/Engine/UI/GUI/Style.cs | 6 - Source/Engine/UI/GUI/Tooltip.cs | 2 +- 83 files changed, 598 insertions(+), 619 deletions(-) delete mode 100644 Source/Engine/Render2D/FallbackTextUtils.cs diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs index 1091c9cef..259be104b 100644 --- a/Source/Editor/Content/GUI/ContentView.cs +++ b/Source/Editor/Content/GUI/ContentView.cs @@ -598,7 +598,7 @@ namespace FlaxEditor.Content.GUI // Check if it's an empty thing if (_items.Count == 0) { - FallbackTextUtils.DrawText(style.FontSmall, IsSearching ? "No results" : "Empty", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, IsSearching ? "No results" : "Empty", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 0842dfff7..604caa704 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -745,7 +745,7 @@ namespace FlaxEditor.Content // Draw short name Render2D.PushClip(ref textRect); - FallbackTextUtils.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f); + Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f); Render2D.PopClip(); } diff --git a/Source/Editor/Content/Tree/ContentTreeNode.cs b/Source/Editor/Content/Tree/ContentTreeNode.cs index 2f378e7f5..e2cd1e771 100644 --- a/Source/Editor/Content/Tree/ContentTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentTreeNode.cs @@ -150,8 +150,8 @@ namespace FlaxEditor.Content var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = FallbackTextUtils.GetCharPosition(font, text, ranges[i].StartIndex); - var end = FallbackTextUtils.GetCharPosition(font, text, ranges[i].EndIndex); + var start = font.GetCharPosition(text, ranges[i].StartIndex); + var end = font.GetCharPosition(text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs b/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs index 66c1c791f..4099e5aee 100644 --- a/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/MeshReferenceEditor.cs @@ -109,7 +109,7 @@ namespace FlaxEditor.CustomEditors.Dedicated { // Draw name Render2D.PushClip(nameRect); - FallbackTextUtils.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); // Draw deselect button @@ -118,7 +118,7 @@ namespace FlaxEditor.CustomEditors.Dedicated else { // Draw info - FallbackTextUtils.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); } // Draw picker button diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 017fd7655..d7bfbbad7 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -42,7 +42,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Add script button var buttonText = "Add script"; - var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); + var textSize = Style.Current.FontMedium.MeasureText(buttonText); float addScriptButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var buttonHeight = (textSize.Y < 18) ? 18 : textSize.Y + 4; _addScriptsButton = new Button @@ -86,7 +86,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var size = Size; // Info - FallbackTextUtils.DrawText(style.FontSmall, "Drag scripts here", new Rectangle(2, _addScriptsButton.Height + 4, size.X - 4, size.Y - 4 - 20), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, "Drag scripts here", new Rectangle(2, _addScriptsButton.Height + 4, size.X - 4, size.Y - 4 - 20), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); // Check if drag is over if (IsDragOver && _dragHandlers != null && _dragHandlers.HasValidDrag) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index fa4379db1..7b9b65c5c 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -226,7 +226,7 @@ namespace FlaxEditor.CustomEditors.Dedicated if (!enabled) color *= 0.6f; Render2D.DrawSprite(tab._customIcon, iconRect, color); - FallbackTextUtils.DrawText(style.FontMedium, tab._customText, new Rectangle(0, iconSize, size.X, textHeight), color, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, tab._customText, new Rectangle(0, iconSize, size.X, textHeight), color, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index 4efbee259..296560507 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -423,7 +423,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Set control type button var space = layout.Space(20); var buttonText = "Set Type"; - var textSize = FallbackTextUtils.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); float setTypeButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var setTypeButton = new Button { diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index d8d16027b..4c153e759 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -100,7 +100,7 @@ namespace FlaxEditor.CustomEditors.Editors _linkButton.Clicked += ToggleLink; ToggleEnabled(); SetLinkStyle(); - var textSize = FallbackTextUtils.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, LinkedLabel.Text.Value); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(LinkedLabel.Text.Value); _linkButton.LocalX += textSize.X + 10; LinkedLabel.SetupContextMenu += (label, menu, editor) => { diff --git a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs index 2f64ea159..731da3817 100644 --- a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs @@ -199,7 +199,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Draw name Render2D.PushClip(nameRect); - FallbackTextUtils.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _valueName, nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); // Draw deselect button @@ -208,7 +208,7 @@ namespace FlaxEditor.CustomEditors.Editors else { // Draw info - FallbackTextUtils.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center); } // Draw picker button diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs index bbece7e04..dbd5d124c 100644 --- a/Source/Editor/CustomEditors/Editors/TagEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs @@ -631,7 +631,7 @@ namespace FlaxEditor.CustomEditors.Editors TooltipText = "Edit...", Parent = _label, }; - var textSize = FallbackTextUtils.MeasureText(FlaxEngine.GUI.Style.Current.FontMedium, buttonText); + var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); if (textSize.Y > button.Width) button.Width = textSize.Y + 2; diff --git a/Source/Editor/CustomEditors/Editors/TypeEditor.cs b/Source/Editor/CustomEditors/Editors/TypeEditor.cs index 4d0b90383..38800a738 100644 --- a/Source/Editor/CustomEditors/Editors/TypeEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TypeEditor.cs @@ -160,13 +160,13 @@ namespace FlaxEditor.CustomEditors.Editors // Draw name Render2D.PushClip(nameRect); - FallbackTextUtils.DrawText(style.FontMedium, _valueName, nameRect, style.Foreground, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _valueName, nameRect, style.Foreground, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); } else { // Draw info - FallbackTextUtils.DrawText(style.FontMedium, "-", nameRect, Color.OrangeRed, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "-", nameRect, Color.OrangeRed, TextAlignment.Near, TextAlignment.Center); } // Draw picker button diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs index 2abe5db38..84f58daf1 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -139,7 +139,7 @@ namespace FlaxEditor.GUI float sizeForTextLeft = Width - button1Rect.Right; if (sizeForTextLeft > 30) { - FallbackTextUtils.DrawText( + Render2D.DrawText( style.FontSmall, Validator.SelectedItem.ShortName, new Rectangle(button1Rect.Right + 2, 0, sizeForTextLeft, ButtonsSize), @@ -161,7 +161,7 @@ namespace FlaxEditor.GUI var name = Validator.SelectedAsset.GetType().Name; if (Validator.SelectedAsset.IsVirtual) name += " (virtual)"; - FallbackTextUtils.DrawText( + Render2D.DrawText( style.FontSmall, name, new Rectangle(button1Rect.Right + 2, 0, sizeForTextLeft, ButtonsSize), @@ -174,7 +174,7 @@ namespace FlaxEditor.GUI { // No element selected Render2D.FillRectangle(iconRect, style.BackgroundNormal); - FallbackTextUtils.DrawText(style.FontMedium, "No asset\nselected", iconRect, Color.Orange, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize); + Render2D.DrawText(style.FontMedium, "No asset\nselected", iconRect, Color.Orange, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize); } // Check if drag is over diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index 8f3a7cb4b..0417cc7e3 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -554,7 +554,7 @@ namespace FlaxEditor.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - FallbackTextUtils.DrawText(Font.GetFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + Render2D.DrawText(Font.GetFont(), text, textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); } diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index 035e997e9..e371f7c4b 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -128,12 +128,12 @@ namespace FlaxEditor.GUI.ContextMenu base.Draw(); // Draw text - FallbackTextUtils.DrawText(style.FontMedium, Text, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, Text, textRect, textColor, TextAlignment.Near, TextAlignment.Center); if (!string.IsNullOrEmpty(ShortKeys)) { // Draw short keys - FallbackTextUtils.DrawText(style.FontMedium, ShortKeys, textRect, textColor, TextAlignment.Far, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, ShortKeys, textRect, textColor, TextAlignment.Far, TextAlignment.Center); } // Draw icon @@ -235,9 +235,9 @@ namespace FlaxEditor.GUI.ContextMenu float width = 20; if (style.FontMedium) { - width += FallbackTextUtils.MeasureText(style.FontMedium, Text).X; + width += style.FontMedium.MeasureText(Text).X; if (!string.IsNullOrEmpty(ShortKeys)) - width += 40 + FallbackTextUtils.MeasureText(style.FontMedium, ShortKeys).X; + width += 40 + style.FontMedium.MeasureText(ShortKeys).X; } return Mathf.Max(width, base.MinimumWidth); diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index f4839acd5..22deec120 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -832,7 +832,7 @@ namespace FlaxEditor.GUI 50, LabelsSize ); - FallbackTextUtils.DrawText(_labelsFont, label, labelRect, _labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f); + Render2D.DrawText(_labelsFont, label, labelRect, _labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f); } } } diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index 8b1881faf..27878a763 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -281,33 +281,33 @@ namespace FlaxEditor.GUI.Dialogs // RGBA var rgbaR = new Rectangle(_cRed.Left - ChannelTextWidth, _cRed.Y, 10000, _cRed.Height); - FallbackTextUtils.DrawText(style.FontMedium, "R", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "R", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); rgbaR.Location.Y = _cGreen.Y; - FallbackTextUtils.DrawText(style.FontMedium, "G", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "G", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); rgbaR.Location.Y = _cBlue.Y; - FallbackTextUtils.DrawText(style.FontMedium, "B", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "B", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); rgbaR.Location.Y = _cAlpha.Y; - FallbackTextUtils.DrawText(style.FontMedium, "A", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "A", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); // HSV left var hsvHl = new Rectangle(_cHue.Left - ChannelTextWidth, _cHue.Y, 10000, _cHue.Height); - FallbackTextUtils.DrawText(style.FontMedium, "H", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "H", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); hsvHl.Location.Y = _cSaturation.Y; - FallbackTextUtils.DrawText(style.FontMedium, "S", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "S", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); hsvHl.Location.Y = _cValue.Y; - FallbackTextUtils.DrawText(style.FontMedium, "V", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "V", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); // HSV right var hsvHr = new Rectangle(_cHue.Right + 2, _cHue.Y, 10000, _cHue.Height); - FallbackTextUtils.DrawText(style.FontMedium, "°", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "°", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); hsvHr.Location.Y = _cSaturation.Y; - FallbackTextUtils.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); hsvHr.Location.Y = _cValue.Y; - FallbackTextUtils.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); // Hex var hex = new Rectangle(_cHex.Left - 26, _cHex.Y, 10000, _cHex.Height); - FallbackTextUtils.DrawText(style.FontMedium, "Hex", hex, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, "Hex", hex, textColor, TextAlignment.Near, TextAlignment.Center); // Color difference var newRect = new Rectangle(_cOK.X, _cHex.Bottom + PickerMargin, _cCancel.Right - _cOK.Left, 0); diff --git a/Source/Editor/GUI/Docking/DockPanelProxy.cs b/Source/Editor/GUI/Docking/DockPanelProxy.cs index 0b36b85fa..e6e57de8e 100644 --- a/Source/Editor/GUI/Docking/DockPanelProxy.cs +++ b/Source/Editor/GUI/Docking/DockPanelProxy.cs @@ -208,7 +208,7 @@ namespace FlaxEditor.GUI.Docking } // Draw text - FallbackTextUtils.DrawText( + Render2D.DrawText( style.FontMedium, tab.Title, new Rectangle(DockPanel.DefaultLeftTextMargin + iconWidth, 0, Width - DockPanel.DefaultLeftTextMargin - DockPanel.DefaultButtonsSize - 2 * DockPanel.DefaultButtonsMargin, DockPanel.DefaultHeaderHeight), @@ -271,7 +271,7 @@ namespace FlaxEditor.GUI.Docking } // Draw text - FallbackTextUtils.DrawText( + Render2D.DrawText( style.FontMedium, tab.Title, new Rectangle(x + DockPanel.DefaultLeftTextMargin + iconWidth, 0, 10000, DockPanel.DefaultHeaderHeight), diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index 561c898a0..dd4e39ce2 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -488,7 +488,7 @@ namespace FlaxEditor.GUI.Docking { var style = Style.Current; if (style?.FontMedium != null) - _titleSize = FallbackTextUtils.MeasureText(style.FontMedium, _title); + _titleSize = style.FontMedium.MeasureText(_title); } base.PerformLayoutBeforeChildren(); diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index d3c433a47..42d236991 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -86,8 +86,8 @@ namespace FlaxEditor.GUI var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = FallbackTextUtils.GetCharPosition(font, Name, ranges[i].StartIndex); - var end = FallbackTextUtils.GetCharPosition(font, Name, ranges[i].EndIndex); + var start = font.GetCharPosition(Name, ranges[i].StartIndex); + var end = font.GetCharPosition(Name, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + 2, 0, end.X - start.X, Height)); } Visible = true; @@ -136,7 +136,7 @@ namespace FlaxEditor.GUI } // Draw name - FallbackTextUtils.DrawText(style.FontSmall, Name, textRect, TintColor * (Enabled ? style.Foreground : style.ForegroundDisabled), TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, Name, textRect, TintColor * (Enabled ? style.Foreground : style.ForegroundDisabled), TextAlignment.Near, TextAlignment.Center); } /// diff --git a/Source/Editor/GUI/MainMenuButton.cs b/Source/Editor/GUI/MainMenuButton.cs index 43849f4ab..117922361 100644 --- a/Source/Editor/GUI/MainMenuButton.cs +++ b/Source/Editor/GUI/MainMenuButton.cs @@ -72,7 +72,7 @@ namespace FlaxEditor.GUI } // Draw text - FallbackTextUtils.DrawText(style.FontMedium, Text, clientRect, enabled && hasChildItems ? style.Foreground : style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, Text, clientRect, enabled && hasChildItems ? style.Foreground : style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } /// @@ -102,7 +102,7 @@ namespace FlaxEditor.GUI float width = 18; if (style.FontMedium) - width += FallbackTextUtils.MeasureText(style.FontMedium, Text).X; + width += style.FontMedium.MeasureText(Text).X; Width = width; } diff --git a/Source/Editor/GUI/NavigationButton.cs b/Source/Editor/GUI/NavigationButton.cs index 8dedf72fd..18e862304 100644 --- a/Source/Editor/GUI/NavigationButton.cs +++ b/Source/Editor/GUI/NavigationButton.cs @@ -57,7 +57,7 @@ namespace FlaxEditor.GUI } // Draw text - FallbackTextUtils.DrawText(style.FontMedium, Text, textRect, EnabledInHierarchy ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, Text, textRect, EnabledInHierarchy ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } /// @@ -67,7 +67,7 @@ namespace FlaxEditor.GUI if (style.FontMedium) { - Width = FallbackTextUtils.MeasureText(style.FontMedium, Text).X + 2 * DefaultMargin; + Width = style.FontMedium.MeasureText(Text).X + 2 * DefaultMargin; } } } diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index 9868ab456..43785c735 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -38,7 +38,7 @@ namespace FlaxEditor.GUI { Depth = -1; - var mediumHeight = FallbackTextUtils.GetMaxHeight(Style.Current.FontMedium); + var mediumHeight = Style.Current.FontMedium.GetMaxHeight(); if (Height < mediumHeight) Height = mediumHeight + 4; } @@ -99,7 +99,7 @@ namespace FlaxEditor.GUI rect.Width -= leftDepthMargin; Render2D.PushClip(rect); - FallbackTextUtils.DrawText(style.FontMedium, text, rect, style.Foreground, column.CellAlignment, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, text, rect, style.Foreground, column.CellAlignment, TextAlignment.Center); Render2D.PopClip(); x += width; diff --git a/Source/Editor/GUI/StatusBar.cs b/Source/Editor/GUI/StatusBar.cs index 7770af9a1..f8f7ae839 100644 --- a/Source/Editor/GUI/StatusBar.cs +++ b/Source/Editor/GUI/StatusBar.cs @@ -56,7 +56,7 @@ namespace FlaxEditor.GUI Render2D.DrawSprite(style.StatusBarSizeGrip, new Rectangle(Width - 12, 10, 12, 12), style.Foreground); // Draw status text - FallbackTextUtils.DrawText(style.FontSmall, Text, new Rectangle(4, 0, Width - 20, Height), TextColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, Text, new Rectangle(4, 0, Width - 20, Height), TextColor, TextAlignment.Near, TextAlignment.Center); } } } diff --git a/Source/Editor/GUI/StyleValueEditor.cs b/Source/Editor/GUI/StyleValueEditor.cs index db6696152..a89902177 100644 --- a/Source/Editor/GUI/StyleValueEditor.cs +++ b/Source/Editor/GUI/StyleValueEditor.cs @@ -157,7 +157,7 @@ namespace FlaxEditor.GUI { Rectangle textRectangle = r; textRectangle.X = 4; - FallbackTextUtils.DrawText(style.FontMedium, "No Style", textRectangle, style.Foreground); + Render2D.DrawText(style.FontMedium, "No Style", textRectangle, style.Foreground); } } diff --git a/Source/Editor/GUI/Table.cs b/Source/Editor/GUI/Table.cs index 2358557c3..1d22ddb15 100644 --- a/Source/Editor/GUI/Table.cs +++ b/Source/Editor/GUI/Table.cs @@ -130,7 +130,7 @@ namespace FlaxEditor.GUI var style = Style.Current; var font = column.TitleFont ?? style.FontMedium; - FallbackTextUtils.DrawText(font, column.Title, rect, column.TitleColor, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(font, column.Title, rect, column.TitleColor, TextAlignment.Center, TextAlignment.Center); if (columnIndex < _columns.Length - 1) { diff --git a/Source/Editor/GUI/Tabs/Tabs.cs b/Source/Editor/GUI/Tabs/Tabs.cs index 5995c7ef5..3c70363e7 100644 --- a/Source/Editor/GUI/Tabs/Tabs.cs +++ b/Source/Editor/GUI/Tabs/Tabs.cs @@ -98,7 +98,7 @@ namespace FlaxEditor.GUI.Tabs // Draw text if (!string.IsNullOrEmpty(Tab.Text)) { - FallbackTextUtils.DrawText(style.FontMedium, Tab.Text, new Rectangle(tabRect.X + textOffset, tabRect.Y, tabRect.Width - textOffset, tabRect.Height), style.Foreground, Tabs.TabsTextHorizontalAlignment, Tabs.TabsTextVerticalAlignment); + Render2D.DrawText(style.FontMedium, Tab.Text, new Rectangle(tabRect.X + textOffset, tabRect.Y, tabRect.Width - textOffset, tabRect.Height), style.Foreground, Tabs.TabsTextHorizontalAlignment, Tabs.TabsTextVerticalAlignment); } } } diff --git a/Source/Editor/GUI/Timeline/GUI/Background.cs b/Source/Editor/GUI/Timeline/GUI/Background.cs index 05c1121c0..b9eff562a 100644 --- a/Source/Editor/GUI/Timeline/GUI/Background.cs +++ b/Source/Editor/GUI/Timeline/GUI/Background.cs @@ -301,7 +301,7 @@ namespace FlaxEditor.GUI.Timeline.GUI default: throw new ArgumentOutOfRangeException(); } var labelRect = new Rectangle(x + 2, -verticalLinesHeaderExtend * 0.8f + timeAxisHeaderOffset, 50, verticalLinesHeaderExtend); - FallbackTextUtils.DrawText(style.FontSmall, labelText, labelRect, labelColor, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.8f); + Render2D.DrawText(style.FontSmall, labelText, labelRect, labelColor, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.8f); } } } diff --git a/Source/Editor/GUI/Timeline/Track.cs b/Source/Editor/GUI/Timeline/Track.cs index 20be8f0c2..f38917aa6 100644 --- a/Source/Editor/GUI/Timeline/Track.cs +++ b/Source/Editor/GUI/Timeline/Track.cs @@ -965,7 +965,7 @@ namespace FlaxEditor.GUI.Timeline } // Draw text - FallbackTextUtils.DrawText(style.FontSmall, Title ?? Name, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, Title ?? Name, textRect, textColor, TextAlignment.Near, TextAlignment.Center); // Disabled overlay DrawDisabled = Mute || (ParentTrack != null && ParentTrack.DrawDisabled); diff --git a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs index dd79922ac..63787df2c 100644 --- a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs @@ -345,7 +345,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks if (_previewValue != null) { // Based on Track.Draw for track text placement - var left = _xOffset + 16 + FallbackTextUtils.MeasureText(Style.Current.FontSmall, Title ?? Name).X; + var left = _xOffset + 16 + Style.Current.FontSmall.MeasureText(Title ?? Name).X; if (Icon.IsValid) left += 18; if (IsExpanded) diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index c4f4603e2..e839a7356 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -136,7 +136,7 @@ namespace FlaxEditor.GUI if (!string.IsNullOrEmpty(_text)) { textRect.Size.X = Width - DefaultMargin - textRect.Left; - FallbackTextUtils.DrawText(style.FontMedium, _text, textRect, enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _text, textRect, enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } } @@ -151,7 +151,7 @@ namespace FlaxEditor.GUI if (hasSprite) width += iconSize; if (!string.IsNullOrEmpty(_text) && style.FontMedium) - width += FallbackTextUtils.MeasureText(style.FontMedium, _text).X + (hasSprite ? DefaultMargin : 0); + width += style.FontMedium.MeasureText(_text).X + (hasSprite ? DefaultMargin : 0); Width = width; } diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 6680e3608..703079469 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -575,7 +575,7 @@ namespace FlaxEditor.GUI.Tree var font = TextFont.GetFont(); if (font) { - _textWidth = FallbackTextUtils.MeasureText(font, _text).X; + _textWidth = font.MeasureText(_text).X; _textChanged = false; } } @@ -656,7 +656,7 @@ namespace FlaxEditor.GUI.Tree } // Draw text - FallbackTextUtils.DrawText(TextFont.GetFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(TextFont.GetFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center); // Draw drag and drop effect if (IsDragOver && _tree.DraggedOverNode == this) diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index dc376a0a2..bf35105a5 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; +using FlaxEditor.Content.Settings; using FlaxEditor.Modules; using FlaxEngine; using FlaxEngine.GUI; @@ -224,7 +225,11 @@ namespace FlaxEditor.Options } } - FallbackTextUtils.Fallbacks = FallbackFonts.Create(Options.Interface.Fallbacks); + var graphicsSetttings = GameSettings.Load(); + if (graphicsSetttings.EnableFontFallback && graphicsSetttings.FallbackFonts == null) + { + Render2D.FallbackFonts = graphicsSetttings.FallbackFonts = FontFallbackList.Create(Options.Interface.Fallbacks); + } } /// diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index eefd7f4dd..f64e46385 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -142,8 +142,8 @@ namespace FlaxEditor.SceneGraph.GUI var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = FallbackTextUtils.GetCharPosition(font, text, ranges[i].StartIndex); - var end = FallbackTextUtils.GetCharPosition(font, text, ranges[i].EndIndex); + var start = font.GetCharPosition(text, ranges[i].StartIndex); + var end = font.GetCharPosition(text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index be9a96a7d..621c7c25e 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -337,7 +337,7 @@ namespace FlaxEditor.Surface.Archetypes _textRect = new Rectangle(Float2.Zero, Size); var style = Style.Current; - var titleSize = FallbackTextUtils.MeasureText(style.FontLarge, Title); + var titleSize = style.FontLarge.MeasureText(Title); var width = Mathf.Max(100, titleSize.X + 50); Resize(width, 0); titleSize.X += 8.0f; @@ -379,7 +379,7 @@ namespace FlaxEditor.Surface.Archetypes } // Name - FallbackTextUtils.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); } /// @@ -1128,7 +1128,7 @@ namespace FlaxEditor.Surface.Archetypes } // Name - FallbackTextUtils.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, Title, _textRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); // Close button Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) ? style.Foreground : style.ForegroundGrey); @@ -1402,7 +1402,7 @@ namespace FlaxEditor.Surface.Archetypes { Title = StateTitle; var style = Style.Current; - var titleSize = FallbackTextUtils.MeasureText(style.FontLarge, Title); + var titleSize = style.FontLarge.MeasureText(Title); var width = Mathf.Max(100, titleSize.X + 50); Resize(width, 0); titleSize.X += 8.0f; diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index dae16b425..40a3d2a63 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -77,7 +77,7 @@ namespace FlaxEditor.Surface.Archetypes Title = asset?.ShortName ?? "Animation"; var style = Style.Current; - Resize(Mathf.Max(230, FallbackTextUtils.MeasureText(style.FontLarge, Title).X + 30), 160); + Resize(Mathf.Max(230, style.FontLarge.MeasureText(Title).X + 30), 160); } /// diff --git a/Source/Editor/Surface/Archetypes/BehaviorTree.cs b/Source/Editor/Surface/Archetypes/BehaviorTree.cs index b240685cc..f8cd7bb5a 100644 --- a/Source/Editor/Surface/Archetypes/BehaviorTree.cs +++ b/Source/Editor/Surface/Archetypes/BehaviorTree.cs @@ -100,7 +100,7 @@ namespace FlaxEditor.Surface.Archetypes _debugRelevant = Behavior.GetNodeDebugRelevancy(instance, behavior); _debugInfo = Behavior.GetNodeDebugInfo(instance, behavior); if (!string.IsNullOrEmpty(_debugInfo)) - _debugInfoSize = FallbackTextUtils.MeasureText(Style.Current.FontSmall, _debugInfo); + _debugInfoSize = Style.Current.FontSmall.MeasureText(_debugInfo); } } @@ -184,7 +184,7 @@ namespace FlaxEditor.Surface.Archetypes if (!string.IsNullOrEmpty(_debugInfo)) { var style = Style.Current; - FallbackTextUtils.DrawText(style.FontSmall, _debugInfo, new Rectangle(4, _headerRect.Bottom + 4, _debugInfoSize), style.Foreground); + Render2D.DrawText(style.FontSmall, _debugInfo, new Rectangle(4, _headerRect.Bottom + 4, _debugInfoSize), style.Foreground); } // Debug relevancy outline @@ -487,7 +487,7 @@ namespace FlaxEditor.Surface.Archetypes var height = 0.0f; var titleLabelFont = Style.Current.FontLarge; width = Mathf.Max(width, 100.0f); - width = Mathf.Max(width, FallbackTextUtils.MeasureText(titleLabelFont, Title).X + 30); + width = Mathf.Max(width, titleLabelFont.MeasureText(Title).X + 30); if (_debugInfoSize.X > 0) { width = Mathf.Max(width, _debugInfoSize.X + 8.0f); diff --git a/Source/Editor/Surface/Archetypes/ParticleModules.cs b/Source/Editor/Surface/Archetypes/ParticleModules.cs index 6082e212e..e5be3d35d 100644 --- a/Source/Editor/Surface/Archetypes/ParticleModules.cs +++ b/Source/Editor/Surface/Archetypes/ParticleModules.cs @@ -113,7 +113,7 @@ namespace FlaxEditor.Surface.Archetypes var idx = (int)ModuleType; var headerRect = new Rectangle(0, 0, Width, 16.0f); //Render2D.FillRectangle(headerRect, Color.Red); - FallbackTextUtils.DrawText(style.FontMedium, Title, headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, Title, headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); DrawChildren(); diff --git a/Source/Editor/Surface/Archetypes/Particles.cs b/Source/Editor/Surface/Archetypes/Particles.cs index bf7828960..40b38f7ce 100644 --- a/Source/Editor/Surface/Archetypes/Particles.cs +++ b/Source/Editor/Surface/Archetypes/Particles.cs @@ -154,7 +154,7 @@ namespace FlaxEditor.Surface.Archetypes if (headerRect.Contains(mousePosition)) headerColor *= 1.07f; Render2D.FillRectangle(headerRect, headerColor); - FallbackTextUtils.DrawText(style.FontLarge, Names[idx], headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, Names[idx], headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); DrawChildren(); } @@ -194,7 +194,7 @@ namespace FlaxEditor.Surface.Archetypes if (_headerRect.Contains(ref _mousePosition)) headerColor *= 1.07f; Render2D.FillRectangle(_headerRect, headerColor); - FallbackTextUtils.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); DrawChildren(); diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index f4976be67..207875a92 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -200,8 +200,8 @@ namespace FlaxEditor.Surface.ContextMenu var font = style.FontSmall; for (int i = 0; i < ranges.Length; i++) { - var start = FallbackTextUtils.GetCharPosition(font, _archetype.Title, ranges[i].StartIndex); - var end = FallbackTextUtils.GetCharPosition(font, _archetype.Title, ranges[i].EndIndex); + var start = font.GetCharPosition(_archetype.Title, ranges[i].StartIndex); + var end = font.GetCharPosition(_archetype.Title, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); if (ranges[i].StartIndex <= 0) @@ -222,8 +222,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = FallbackTextUtils.GetCharPosition(font, _archetype.Title, 0); - var end = FallbackTextUtils.GetCharPosition(font, _archetype.Title, _archetype.Title.Length - 1); + var start = font.GetCharPosition(_archetype.Title, 0); + var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); _isFullMatch = true; Visible = true; @@ -237,8 +237,8 @@ namespace FlaxEditor.Surface.ContextMenu _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; - var start = FallbackTextUtils.GetCharPosition(font, _archetype.Title, 0); - var end = FallbackTextUtils.GetCharPosition(font, _archetype.Title, _archetype.Title.Length - 1); + var start = font.GetCharPosition(_archetype.Title, 0); + var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height)); Visible = true; @@ -283,19 +283,19 @@ namespace FlaxEditor.Surface.ContextMenu } // Draw name - FallbackTextUtils.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); if (_archetype.SubTitle != null) { - var titleLength = FallbackTextUtils.MeasureText(style.FontSmall, _archetype.Title).X; + var titleLength = style.FontSmall.MeasureText(_archetype.Title).X; var subTitleRect = new Rectangle(textRect.X + titleLength, textRect.Y, textRect.Width - titleLength, textRect.Height); - FallbackTextUtils.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } // Reset transform and draw score mark if (showScoreHit) { Render2D.PopTransform(); - FallbackTextUtils.DrawText(style.FontSmall, "> ", textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, "> ", textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } } diff --git a/Source/Editor/Surface/Elements/InputBox.cs b/Source/Editor/Surface/Elements/InputBox.cs index a5a99b932..2047bcd1a 100644 --- a/Source/Editor/Surface/Elements/InputBox.cs +++ b/Source/Editor/Surface/Elements/InputBox.cs @@ -1428,7 +1428,7 @@ namespace FlaxEditor.Surface.Elements if (_defaultValueEditor != null) { - _defaultValueEditor.Location = new Float2(X + Width + 8 + FallbackTextUtils.MeasureText(Style.Current.FontSmall, Text).X, Y); + _defaultValueEditor.Location = new Float2(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y); } } @@ -1443,7 +1443,7 @@ namespace FlaxEditor.Surface.Elements // Draw text var style = Style.Current; var rect = new Rectangle(Width + 4, 0, 1410, Height); - FallbackTextUtils.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } /// @@ -1635,7 +1635,7 @@ namespace FlaxEditor.Surface.Elements { if (DefaultValueEditors[i].CanUse(this, ref _currentType)) { - var bounds = new Rectangle(X + Width + 8 + FallbackTextUtils.MeasureText(Style.Current.FontSmall, Text).X, Y, 90, Height); + var bounds = new Rectangle(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y, 90, Height); _editor = DefaultValueEditors[i]; try { diff --git a/Source/Editor/Surface/Elements/OutputBox.cs b/Source/Editor/Surface/Elements/OutputBox.cs index 5fb123700..497e2001a 100644 --- a/Source/Editor/Surface/Elements/OutputBox.cs +++ b/Source/Editor/Surface/Elements/OutputBox.cs @@ -189,7 +189,7 @@ namespace FlaxEditor.Surface.Elements // Draw text var style = Style.Current; var rect = new Rectangle(-100, 0, 100 - 2, Height); - FallbackTextUtils.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Far, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Far, TextAlignment.Center); } } } diff --git a/Source/Editor/Surface/Elements/TextView.cs b/Source/Editor/Surface/Elements/TextView.cs index c52667fe3..284497396 100644 --- a/Source/Editor/Surface/Elements/TextView.cs +++ b/Source/Editor/Surface/Elements/TextView.cs @@ -25,7 +25,7 @@ namespace FlaxEditor.Surface.Elements var style = Style.Current; var color = Enabled ? style.Foreground : style.ForegroundDisabled; - FallbackTextUtils.DrawText(style.FontSmall, Archetype.Text, new Rectangle(Float2.Zero, Size), color, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontSmall, Archetype.Text, new Rectangle(Float2.Zero, Size), color, TextAlignment.Near, TextAlignment.Center); } } } diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index 26cc912ab..aad45190e 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -169,7 +169,7 @@ namespace FlaxEditor.Surface // Header Render2D.FillRectangle(_headerRect, headerColor); - FallbackTextUtils.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); // Close button Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index df706faa6..780ef81f0 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -199,7 +199,7 @@ namespace FlaxEditor.Surface continue; if (child is InputBox inputBox) { - var boxWidth = FallbackTextUtils.MeasureText(boxLabelFont, inputBox.Text).X + 20; + var boxWidth = boxLabelFont.MeasureText(inputBox.Text).X + 20; if (inputBox.DefaultValueEditor != null) boxWidth += inputBox.DefaultValueEditor.Width + 4; leftWidth = Mathf.Max(leftWidth, boxWidth); @@ -207,7 +207,7 @@ namespace FlaxEditor.Surface } else if (child is OutputBox outputBox) { - rightWidth = Mathf.Max(rightWidth, FallbackTextUtils.MeasureText(boxLabelFont, outputBox.Text).X + 20); + rightWidth = Mathf.Max(rightWidth, boxLabelFont.MeasureText(outputBox.Text).X + 20); rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } else if (child is Control control) @@ -225,7 +225,7 @@ namespace FlaxEditor.Surface } } width = Mathf.Max(width, leftWidth + rightWidth + 10); - width = Mathf.Max(width, FallbackTextUtils.MeasureText(titleLabelFont, Title).X + 30); + width = Mathf.Max(width, titleLabelFont.MeasureText(Title).X + 30); height = Mathf.Max(height, Mathf.Max(leftHeight, rightHeight)); Resize(width, height); } @@ -1027,7 +1027,7 @@ namespace FlaxEditor.Surface if (_headerRect.Contains(ref _mousePosition)) headerColor *= 1.07f; Render2D.FillRectangle(_headerRect, headerColor); - FallbackTextUtils.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); // Close button if ((Archetype.Flags & NodeFlags.NoCloseButton) == 0 && Surface.CanEdit) diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index d36c3b6ed..1b46a42be 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -147,7 +147,7 @@ namespace FlaxEditor.Tools.Foliage Parent = _noFoliagePanel, Enabled = false }; - var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); + var textSize = Style.Current.FontMedium.MeasureText(buttonText); if (_createNewFoliage.Width < textSize.X) { _createNewFoliage.LocalX -= (textSize.X - _createNewFoliage.Width) / 2; diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index e64a364f0..d51915acf 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -105,7 +105,7 @@ namespace FlaxEditor.Tools.Terrain Parent = _noTerrainPanel, Enabled = false }; - var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); + var textSize = Style.Current.FontMedium.MeasureText(buttonText); if (_createTerrainButton.Width < textSize.X) { _createTerrainButton.LocalX -= (textSize.X - _createTerrainButton.Width) / 2; diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 515514131..c49392d01 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -548,9 +548,9 @@ namespace FlaxEditor.Viewport #region Camera settings widget var largestText = "Relative Panning"; - var textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, largestText); + var textSize = Style.Current.FontMedium.MeasureText(largestText); var xLocationForExtras = textSize.X + 5; - var cameraSpeedTextWidth = FallbackTextUtils.MeasureText(Style.Current.FontMedium, "0.00").X; + var cameraSpeedTextWidth = Style.Current.FontMedium.MeasureText("0.00").X; // Camera Settings Widget _cameraWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); @@ -801,7 +801,7 @@ namespace FlaxEditor.Viewport #region View mode widget largestText = "Brightness"; - textSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, largestText); + textSize = Style.Current.FontMedium.MeasureText(largestText); xLocationForExtras = textSize.X + 5; var viewMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft); @@ -1233,8 +1233,8 @@ namespace FlaxEditor.Viewport color = Color.Yellow; var text = string.Format("FPS: {0}", fps); var font = Style.Current.FontMedium; - FallbackTextUtils.DrawText(font, text, new Rectangle(Float2.One, Size), Color.Black); - FallbackTextUtils.DrawText(font, text, new Rectangle(Float2.Zero, Size), color); + Render2D.DrawText(font, text, new Rectangle(Float2.One, Size), Color.Black); + Render2D.DrawText(font, text, new Rectangle(Float2.Zero, Size), color); } } @@ -1814,7 +1814,7 @@ namespace FlaxEditor.Viewport { var bounds = new Rectangle(Float2.Zero, Size); Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f)); - FallbackTextUtils.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Viewport/Previews/AnimationPreview.cs b/Source/Editor/Viewport/Previews/AnimationPreview.cs index c4ada83c4..1679a36bc 100644 --- a/Source/Editor/Viewport/Previews/AnimationPreview.cs +++ b/Source/Editor/Viewport/Previews/AnimationPreview.cs @@ -96,11 +96,11 @@ namespace FlaxEditor.Viewport.Previews var skinnedModel = SkinnedModel; if (skinnedModel == null) { - FallbackTextUtils.DrawText(style.FontLarge, "Missing Base Model", new Rectangle(Float2.Zero, Size), Color.Red, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapWords); + Render2D.DrawText(style.FontLarge, "Missing Base Model", new Rectangle(Float2.Zero, Size), Color.Red, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapWords); } else if (!skinnedModel.IsLoaded) { - FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Viewport/Previews/ModelPreview.cs b/Source/Editor/Viewport/Previews/ModelPreview.cs index b31776bf6..a6496aafe 100644 --- a/Source/Editor/Viewport/Previews/ModelPreview.cs +++ b/Source/Editor/Viewport/Previews/ModelPreview.cs @@ -409,8 +409,8 @@ namespace FlaxEditor.Viewport.Previews } var font = Style.Current.FontMedium; var pos = new Float2(10, 50); - FallbackTextUtils.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); - FallbackTextUtils.DrawText(font, text, new Rectangle(pos, Size), Color.White); + Render2D.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); + Render2D.DrawText(font, text, new Rectangle(pos, Size), Color.White); } } diff --git a/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs b/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs index a6eb721ce..c6ce5a7b5 100644 --- a/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs +++ b/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs @@ -254,7 +254,7 @@ namespace FlaxEditor.Viewport.Previews if (_showParticlesCounter) { var count = _previewEffect.ParticlesCount; - FallbackTextUtils.DrawText( + Render2D.DrawText( Style.Current.FontSmall, "Particles: " + count, new Rectangle(Float2.Zero, Size), diff --git a/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs b/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs index 6c02c019a..8bcc506d9 100644 --- a/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs +++ b/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs @@ -161,8 +161,8 @@ namespace FlaxEditor.Viewport.Previews } var font = Style.Current.FontMedium; var pos = new Float2(10, 50); - FallbackTextUtils.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); - FallbackTextUtils.DrawText(font, text, new Rectangle(pos, Size), Color.White); + Render2D.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); + Render2D.DrawText(font, text, new Rectangle(pos, Size), Color.White); } } diff --git a/Source/Editor/Viewport/Previews/TexturePreview.cs b/Source/Editor/Viewport/Previews/TexturePreview.cs index 5e7594e9c..ec10bb019 100644 --- a/Source/Editor/Viewport/Previews/TexturePreview.cs +++ b/Source/Editor/Viewport/Previews/TexturePreview.cs @@ -103,7 +103,7 @@ namespace FlaxEditor.Viewport.Previews { var bounds = new Rectangle(Float2.Zero, Size); Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f)); - FallbackTextUtils.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); } Render2D.PopClip(); diff --git a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs index 43f990afb..77e94ae53 100644 --- a/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs +++ b/Source/Editor/Viewport/Widgets/ViewportWidgetButton.cs @@ -123,7 +123,7 @@ namespace FlaxEditor.Viewport.Widgets } // Draw text - FallbackTextUtils.DrawText(style.FontMedium, _text, textRect, style.ForegroundViewport * (IsMouseOver ? 1.0f : 0.9f), TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _text, textRect, style.ForegroundViewport * (IsMouseOver ? 1.0f : 0.9f), TextAlignment.Center, TextAlignment.Center); } /// @@ -163,7 +163,7 @@ namespace FlaxEditor.Viewport.Widgets var style = Style.Current; if (style != null && style.FontMedium) - Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : FallbackTextUtils.MeasureText(style.FontMedium, _text).X, Icon.IsValid); + Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.MeasureText(_text).X, Icon.IsValid); } } } diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index 8c38ed2a9..b059dadcb 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -53,7 +53,7 @@ namespace FlaxEditor.Windows Parent = this }; var buttonText = "Copy version info"; - var fontSize = FallbackTextUtils.MeasureText(Style.Current.FontMedium, buttonText); + var fontSize = Style.Current.FontMedium.MeasureText(buttonText); var copyVersionButton = new Button(Width - fontSize.X - 8, 6, fontSize.X + 4, 20) { Text = buttonText, diff --git a/Source/Editor/Windows/Assets/AnimationGraphWindow.cs b/Source/Editor/Windows/Assets/AnimationGraphWindow.cs index eb4198fd8..12eac22b5 100644 --- a/Source/Editor/Windows/Assets/AnimationGraphWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationGraphWindow.cs @@ -51,7 +51,7 @@ namespace FlaxEditor.Windows.Assets var style = Style.Current; if (_window.Asset == null || !_window.Asset.IsLoaded) { - FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } } diff --git a/Source/Editor/Windows/Assets/AnimationWindow.cs b/Source/Editor/Windows/Assets/AnimationWindow.cs index 042cf2b37..8f88d93f6 100644 --- a/Source/Editor/Windows/Assets/AnimationWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationWindow.cs @@ -61,7 +61,7 @@ namespace FlaxEditor.Windows.Assets var animation = _window.Asset; if (animation == null || !animation.IsLoaded) { - FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } diff --git a/Source/Editor/Windows/Assets/ModelWindow.cs b/Source/Editor/Windows/Assets/ModelWindow.cs index d48966043..c2764a5a6 100644 --- a/Source/Editor/Windows/Assets/ModelWindow.cs +++ b/Source/Editor/Windows/Assets/ModelWindow.cs @@ -48,7 +48,7 @@ namespace FlaxEditor.Windows.Assets var asset = _window.Asset; if (asset == null || !asset.IsLoaded) { - FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } } @@ -645,7 +645,7 @@ namespace FlaxEditor.Windows.Assets if (!Proxy.Window._meshData.RequestMeshData(Proxy.Window._asset)) { Invalidate(); - FallbackTextUtils.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); return; } diff --git a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs index 1e7192d9f..95827240c 100644 --- a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs +++ b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs @@ -50,7 +50,7 @@ namespace FlaxEditor.Windows.Assets var asset = _window.Asset; if (asset == null || !asset.IsLoaded) { - FallbackTextUtils.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } } } @@ -715,7 +715,7 @@ namespace FlaxEditor.Windows.Assets if (!Proxy.Window.RequestMeshData()) { Invalidate(); - FallbackTextUtils.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); return; } diff --git a/Source/Editor/Windows/ContentWindow.Search.cs b/Source/Editor/Windows/ContentWindow.Search.cs index 67b7deddd..a1072d158 100644 --- a/Source/Editor/Windows/ContentWindow.Search.cs +++ b/Source/Editor/Windows/ContentWindow.Search.cs @@ -57,7 +57,7 @@ namespace FlaxEditor.Windows var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - FallbackTextUtils.DrawText(Font.GetFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); + Render2D.DrawText(Font.GetFont(), "View", textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, textScale); Render2D.PopClip(); // Arrow diff --git a/Source/Editor/Windows/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs index 91d4f9d8c..ab0c07ec5 100644 --- a/Source/Editor/Windows/DebugLogWindow.cs +++ b/Source/Editor/Windows/DebugLogWindow.cs @@ -140,11 +140,11 @@ namespace FlaxEditor.Windows Render2D.PushClip(ref clientRect); if (LogCount == 1) { - FallbackTextUtils.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground); + Render2D.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground); } else if (LogCount > 1) { - FallbackTextUtils.DrawText(style.FontMedium, $"{Desc.Title} ({LogCount})", textRect, style.Foreground); + Render2D.DrawText(style.FontMedium, $"{Desc.Title} ({LogCount})", textRect, style.Foreground); } Render2D.PopClip(); } diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 2c0364995..4a8c11176 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -827,7 +827,7 @@ namespace FlaxEditor.Windows if (Camera.MainCamera == null) { var style = Style.Current; - FallbackTextUtils.DrawText(style.FontLarge, "No camera", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, "No camera", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } // Selected UI controls outline @@ -866,8 +866,8 @@ namespace FlaxEditor.Windows var alpha = Mathf.Saturate(-animTime / fadeOutTime); var rect = new Rectangle(new Float2(6), Size - 12); var text = "Press Shift+F11 to unlock the mouse"; - FallbackTextUtils.DrawText(style.FontSmall, text, rect + new Float2(1.0f), style.Background * alpha, TextAlignment.Near, TextAlignment.Far); - FallbackTextUtils.DrawText(style.FontSmall, text, rect, style.Foreground * alpha, TextAlignment.Near, TextAlignment.Far); + Render2D.DrawText(style.FontSmall, text, rect + new Float2(1.0f), style.Background * alpha, TextAlignment.Near, TextAlignment.Far); + Render2D.DrawText(style.FontSmall, text, rect, style.Foreground * alpha, TextAlignment.Near, TextAlignment.Far); } timeout = 1.0f; @@ -884,7 +884,7 @@ namespace FlaxEditor.Windows { var bounds = new Rectangle(Float2.Zero, Size); Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f)); - FallbackTextUtils.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); } } } diff --git a/Source/Editor/Windows/Profiler/SingleChart.cs b/Source/Editor/Windows/Profiler/SingleChart.cs index 25241b4c6..4f36692e5 100644 --- a/Source/Editor/Windows/Profiler/SingleChart.cs +++ b/Source/Editor/Windows/Profiler/SingleChart.cs @@ -138,8 +138,8 @@ namespace FlaxEditor.Windows.Profiler var headerRect = new Rectangle(0, chartHeight, Width, TitleHeight); var headerTextRect = new Rectangle(2, chartHeight, Width - 4, TitleHeight); Render2D.FillRectangle(headerRect, style.BackgroundNormal); - FallbackTextUtils.DrawText(style.FontMedium, Title, headerTextRect, style.ForegroundGrey, TextAlignment.Near, TextAlignment.Center); - FallbackTextUtils.DrawText(style.FontMedium, _sample, headerTextRect, style.Foreground, TextAlignment.Far, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, Title, headerTextRect, style.ForegroundGrey, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _sample, headerTextRect, style.Foreground, TextAlignment.Far, TextAlignment.Center); } private void OnClick(ref Float2 location) diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index f87efdace..59a7a0e26 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -85,12 +85,12 @@ namespace FlaxEditor.Windows.Profiler Render2D.DrawRectangle(bounds, color * 0.5f); if (_nameLength < 0 && style.FontMedium) - _nameLength = FallbackTextUtils.MeasureText(style.FontMedium, _name).X; + _nameLength = style.FontMedium.MeasureText(_name).X; if (_nameLength < bounds.Width + 4) { Render2D.PushClip(bounds); - FallbackTextUtils.DrawText(style.FontMedium, _name, bounds, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _name, bounds, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center); Render2D.PopClip(); } } @@ -115,7 +115,7 @@ namespace FlaxEditor.Windows.Profiler var style = Style.Current; var rect = new Rectangle(Float2.Zero, Size); Render2D.PushClip(rect); - FallbackTextUtils.DrawText(style.FontMedium, Name, rect, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapChars); + Render2D.DrawText(style.FontMedium, Name, rect, Style.Current.Foreground, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapChars); Render2D.PopClip(); } } diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 2fd990861..63ba7b960 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -297,7 +297,7 @@ namespace FlaxEditor.Windows } if (overlayText != null) { - FallbackTextUtils.DrawText(style.FontLarge, overlayText, GetClientArea(), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center, textWrap); + Render2D.DrawText(style.FontLarge, overlayText, GetClientArea(), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center, textWrap); } base.Draw(); diff --git a/Source/Editor/Windows/SplashScreen.cpp b/Source/Editor/Windows/SplashScreen.cpp index cbb51ae3c..cba59191e 100644 --- a/Source/Editor/Windows/SplashScreen.cpp +++ b/Source/Editor/Windows/SplashScreen.cpp @@ -258,7 +258,7 @@ void SplashScreen::OnDraw() return; // Title - const auto titleLength = _titleFont->MeasureText(GetTitle()); + const auto titleLength = _titleFont->MeasureTextInternal(GetTitle()); TextLayoutOptions layout; layout.Bounds = Rectangle(10 * s, 10 * s, width - 10 * s, 50 * s); layout.HorizontalAlignment = TextAlignment::Near; diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs index f86341df3..e2b04669b 100644 --- a/Source/Editor/Windows/ToolboxWindow.cs +++ b/Source/Editor/Windows/ToolboxWindow.cs @@ -272,8 +272,8 @@ namespace FlaxEditor.Windows var textRect = item.TextRect; for (int i = 0; i < ranges.Length; i++) { - var start = FallbackTextUtils.GetCharPosition(font, text, ranges[i].StartIndex); - var end = FallbackTextUtils.GetCharPosition(font, text, ranges[i].EndIndex); + var start = font.GetCharPosition(text, ranges[i].StartIndex); + var end = font.GetCharPosition(text, ranges[i].EndIndex); highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } item.SetHighlights(highlights); diff --git a/Source/Engine/Core/Config/GameSettings.h b/Source/Engine/Core/Config/GameSettings.h index 6813f3ee2..675c26bd1 100644 --- a/Source/Engine/Core/Config/GameSettings.h +++ b/Source/Engine/Core/Config/GameSettings.h @@ -35,15 +35,6 @@ public: API_FIELD(Attributes="EditorOrder(15), EditorDisplay(\"General\")") String CopyrightNotice; - /// - /// The copyright note used for content signing (eg. source code header). - /// - API_FIELD(Attributes = "EditorOrder(1200), EditorDisplay(\"Other Settings\")") - bool EnableFontFallback; - - API_FIELD(Attributes = "EditorOrder(1205), EditorDisplay(\"Other Settings\")") - FontFallbackList* FontFallbacks; - /// /// The default application icon. /// diff --git a/Source/Engine/Core/Config/GraphicsSettings.h b/Source/Engine/Core/Config/GraphicsSettings.h index 81d80cb35..7abfbb048 100644 --- a/Source/Engine/Core/Config/GraphicsSettings.h +++ b/Source/Engine/Core/Config/GraphicsSettings.h @@ -6,6 +6,8 @@ #include "Engine/Graphics/Enums.h" #include "Engine/Graphics/PostProcessSettings.h" +class FontFallbackList; + /// /// Graphics rendering settings. /// @@ -118,6 +120,18 @@ public: API_FIELD(Attributes="EditorOrder(10000), EditorDisplay(\"Post Process Settings\", EditorDisplayAttribute.InlineStyle)") PostProcessSettings PostProcessSettings; + /// + /// + /// + API_FIELD(Attributes = "EditorOrder(12000), EditorDisplay(\"Text Render Settings\", EditorDisplayAttribute.InlineStyle)") + bool EnableFontFallback = true; + + /// + /// + /// + API_FIELD(Attributes = "EditorOrder(12005), EditorDisplay(\"Text Render Settings\", EditorDisplayAttribute.InlineStyle)") + FontFallbackList* FallbackFonts; + private: /// /// Renamed UeeHDRProbes into UseHDRProbes diff --git a/Source/Engine/Graphics/Graphics.cpp b/Source/Engine/Graphics/Graphics.cpp index f91c58cea..20f29288b 100644 --- a/Source/Engine/Graphics/Graphics.cpp +++ b/Source/Engine/Graphics/Graphics.cpp @@ -8,6 +8,7 @@ #include "Engine/Core/Config/GraphicsSettings.h" #include "Engine/Engine/CommandLine.h" #include "Engine/Engine/EngineService.h" +#include "Engine/Render2D/Render2D.h" bool Graphics::UseVSync = false; Quality Graphics::AAQuality = Quality::Medium; @@ -69,6 +70,9 @@ void GraphicsSettings::Apply() Graphics::GIQuality = GIQuality; Graphics::PostProcessSettings = ::PostProcessSettings(); Graphics::PostProcessSettings.BlendWith(PostProcessSettings, 1.0f); + + Render2D::EnableFontFallback = EnableFontFallback; + Render2D::FallbackFonts = FallbackFonts; } void Graphics::DisposeDevice() diff --git a/Source/Engine/Render2D/FallbackTextUtils.cs b/Source/Engine/Render2D/FallbackTextUtils.cs deleted file mode 100644 index 486a354bf..000000000 --- a/Source/Engine/Render2D/FallbackTextUtils.cs +++ /dev/null @@ -1,268 +0,0 @@ - -using System.Runtime.CompilerServices; - -namespace FlaxEngine -{ - /// - /// A collection of functions to handle text rendering with fallback font - /// - public static class FallbackTextUtils - { - public static FallbackFonts Fallbacks - { - get; set; - } = null; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void DrawText(Font font, string text, Color color, ref TextLayoutOptions layout, MaterialBase customMaterial = null, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - Render2D.DrawText(font, Fallbacks, text, color, ref layout, customMaterial); - } - else - { - Render2D.DrawText(font, text, color, ref layout, customMaterial); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void DrawText(Font font, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) - { - var layout = new TextLayoutOptions - { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; - - if (Fallbacks != null && useFallback) - { - Render2D.DrawText(font, Fallbacks, text, color, ref layout); - } - else - { - Render2D.DrawText(font, text, color, ref layout); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void DrawText(Font font, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f, bool useFallback = true) - { - var layout = new TextLayoutOptions - { - Bounds = layoutRect, - HorizontalAlignment = horizontalAlignment, - VerticalAlignment = verticalAlignment, - TextWrapping = textWrapping, - Scale = scale, - BaseLinesGapScale = baseLinesGapScale, - }; - - if (Fallbacks != null && useFallback) - { - Render2D.DrawText(font, Fallbacks, text, color, ref layout, customMaterial); - } - else - { - Render2D.DrawText(font, text, color, ref layout, customMaterial); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 MeasureText(Font font, string text, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.MeasureText(Fallbacks, text); - } - else - { - return font.MeasureText(text); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 MeasureText(Font font, string text, ref TextRange textRange, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.MeasureText(Fallbacks, text, ref textRange); - } - else - { - return font.MeasureText(text, ref textRange); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 MeasureText(Font font, string text, ref TextLayoutOptions layout, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.MeasureText(Fallbacks, text, ref layout); - } - else - { - return font.MeasureText(text, ref layout); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 MeasureText(Font font, string text, ref TextRange textRange, ref TextLayoutOptions layout, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.MeasureText(Fallbacks, text, ref textRange, ref layout); - } - else - { - return font.MeasureText(text, ref textRange, ref layout); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int HitTestText(Font font, string text, Float2 location, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.HitTestText(Fallbacks, text, location); - } - else - { - return font.HitTestText(text, location); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int HitTestText(Font font, string text, ref TextRange textRange, Float2 location, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.HitTestText(Fallbacks, text, ref textRange, location); - } - else - { - return font.HitTestText(text, ref textRange, location); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int HitTestText(Font font, string text, Float2 location, ref TextLayoutOptions layout, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.HitTestText(Fallbacks, text, location, ref layout); - } - else - { - return font.HitTestText(text, location, ref layout); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int HitTestText(Font font, string text, ref TextRange textRange, Float2 location, ref TextLayoutOptions layout, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.HitTestText(Fallbacks, text, ref textRange, location, ref layout); - } - else - { - return font.HitTestText(text, ref textRange, location, ref layout); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 GetCharPosition(Font font, string text, int index, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.GetCharPosition(Fallbacks, text, index); - } - else - { - return font.GetCharPosition(text, index); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.GetCharPosition(Fallbacks, text, ref textRange, index); - } - else - { - return font.GetCharPosition(text, ref textRange, index); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 GetCharPosition(Font font, string text, int index, ref TextLayoutOptions layout, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.GetCharPosition(Fallbacks, text, index, ref layout); - } - else - { - return font.GetCharPosition(text, index, ref layout); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Float2 GetCharPosition(Font font, string text, ref TextRange textRange, int index, ref TextLayoutOptions layout, bool useFallback = true) - { - if (Fallbacks != null && useFallback) - { - return font.GetCharPosition(Fallbacks, text, ref textRange, index, ref layout); - } - else - { - 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.cpp b/Source/Engine/Render2D/Font.cpp index 5d029ca5e..9f28aac01 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -103,6 +103,18 @@ void Font::Invalidate() _characters.Clear(); } +inline API_FUNCTION() float Font::GetMaxHeight(FontFallbackList* fallbacks) const +{ + float height = GetHeight(); + auto& fallbackFonts = fallbacks->GetFontList(GetSize()); + for (int32 i = 0; i < fallbackFonts.Count(); i++) + { + height = Math::Max(height, static_cast(fallbackFonts[i]->GetHeight())); + } + + return height; +} + void Font::ProcessText(const StringView& text, Array& outputLines, const TextLayoutOptions& layout) { float cursorX = 0; @@ -548,7 +560,7 @@ void Font::ProcessText(FontFallbackList* fallbacks, const StringView& text, Arra } } -Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout) +Float2 Font::MeasureTextInternal(const StringView& text, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) @@ -569,7 +581,7 @@ Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout return max; } -Float2 Font::MeasureText(FontFallbackList* fallbacks, const StringView& text, const TextLayoutOptions& layout) +Float2 Font::MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) @@ -590,7 +602,7 @@ Float2 Font::MeasureText(FontFallbackList* fallbacks, const StringView& text, co return max; } -int32 Font::HitTestText(const StringView& text, const Float2& location, const TextLayoutOptions& layout) +int32 Font::HitTestTextInternal(const StringView& text, const Float2& location, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.Length() <= 0) @@ -664,7 +676,7 @@ int32 Font::HitTestText(const StringView& text, const Float2& location, const Te return smallestIndex; } -int32 Font::HitTestText(FontFallbackList* fallbacks, const StringView& text, const Float2& location, const TextLayoutOptions& layout) +int32 Font::HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, const Float2& location, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.Length() <= 0) @@ -764,7 +776,7 @@ int32 Font::HitTestText(FontFallbackList* fallbacks, const StringView& text, con return smallestIndex; } -Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayoutOptions& layout) +Float2 Font::GetCharPositionInternal(const StringView& text, int32 index, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) @@ -818,7 +830,7 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo return rootOffset + Float2(lines.Last().Location.X + lines.Last().Size.X, static_cast((lines.Count() - 1) * baseLinesDistance)); } -Float2 Font::GetCharPosition(FontFallbackList* fallbacks, const StringView& text, int32 index, const TextLayoutOptions& layout) +Float2 Font::GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, int32 index, const TextLayoutOptions& layout) { // Check if there is no need to do anything if (text.IsEmpty()) diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index 6bb2849e2..1fb41d032 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -8,6 +8,7 @@ #include "Engine/Content/AssetReference.h" #include "Engine/Scripting/ScriptingObject.h" #include "TextLayoutOptions.h" +#include "Render2D.h" class FontAsset; class FontFallbackList; @@ -402,7 +403,31 @@ public: public: /// - /// Processes text to get cached lines for rendering. + /// Gets the maximum height among the font and the fallback fonts. + /// + /// The fallback fonts. + /// The maximum height. + API_FUNCTION() float GetMaxHeight(FontFallbackList* fallbacks) const; + + /// + /// Gets the maximum height among the font and the fallback fonts, uses the default font defined in . + /// + /// The fallback fonts. + /// The maximum height. + API_FUNCTION() FORCE_INLINE float GetMaxHeight() const + { + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) + { + return GetMaxHeight(Render2D::FallbackFonts); + } + else + { + return GetHeight(); + } + } + + /// + /// Processes text to get cached lines for rendering, with font fallbacking disabled. /// /// The input text. /// The layout properties. @@ -410,12 +435,12 @@ public: void ProcessText(const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, with font fallbacking disabled. /// /// The input text. /// The layout properties. /// The output lines list. - API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(text, lines, layout); @@ -423,13 +448,13 @@ public: } /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, with font fallbacking disabled. /// /// The input text. /// The input text range (substring range of the input text parameter). /// The layout properties. /// The output lines list. - API_FUNCTION() Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() FORCE_INLINE Array ProcessText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(textRange.Substring(text), lines, layout); @@ -437,7 +462,7 @@ public: } /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, with font fallbacking disabled. /// /// The input text. /// The output lines list. @@ -447,7 +472,7 @@ public: } /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, with font fallbacking disabled. /// /// The input text. /// The input text range (substring range of the input text parameter). @@ -458,7 +483,7 @@ public: } /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, using custom fallback options. /// /// The input text. /// The layout properties. @@ -466,12 +491,12 @@ public: void ProcessText(FontFallbackList* fallbacks, const StringView& text, Array& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, using custom fallback options. /// /// The input text. /// The layout properties. /// The output lines list. - API_FUNCTION() Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(fallbacks, text, lines, layout); @@ -479,13 +504,13 @@ public: } /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, using custom fallback options. /// /// The input text. /// The input text range (substring range of the input text parameter). /// The layout properties. /// The output lines list. - API_FUNCTION() Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() FORCE_INLINE Array ProcessText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) { Array lines; ProcessText(fallbacks, textRange.Substring(text), lines, layout); @@ -493,7 +518,7 @@ public: } /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, using custom fallback options. /// /// The input text. /// The output lines list. @@ -503,7 +528,7 @@ public: } /// - /// Processes text to get cached lines for rendering. + /// Processes text to get cached lines for rendering, using custom fallback options. /// /// The input text. /// The input text range (substring range of the input text parameter). @@ -514,122 +539,293 @@ public: } /// - /// Measures minimum size of the rectangle that will be needed to draw given text. + /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. /// /// The input text to test. /// The layout properties. /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() Float2 MeasureTextInternal(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Measures minimum size of the rectangle that will be needed to draw given text. + /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. /// /// The input text to test. /// The input text range (substring range of the input text parameter). /// The layout properties. /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) { - return MeasureText(textRange.Substring(text), layout); + return MeasureTextInternal(textRange.Substring(text), layout); } /// - /// Measures minimum size of the rectangle that will be needed to draw given text + /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. + /// . + /// The input text to test. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(const StringView& text) + { + return MeasureTextInternal(text, TextLayoutOptions()); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, with font fallbacking disabled. + /// . + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange) + { + return MeasureTextInternal(textRange.Substring(text), TextLayoutOptions()); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. + /// + /// The input text to test. + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return MeasureTextInternal(fallbacks, textRange.Substring(text), layout); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. + /// . + /// The input text to test. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text) + { + return MeasureTextInternal(fallbacks, text, TextLayoutOptions()); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, using custom fallback options. + /// . + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) + { + return MeasureTextInternal(fallbacks, textRange.Substring(text), TextLayoutOptions()); + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . + /// + /// The input text to test. + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout) { + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return MeasureTextInternal(Render2D::FallbackFonts, text, layout); + } + else { + return MeasureTextInternal(text, layout); + } + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The layout properties. + /// The minimum size for that text and fot to render properly. + API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) + { + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return MeasureTextInternal(Render2D::FallbackFonts, textRange.Substring(text), layout); + } + else { + return MeasureTextInternal(textRange.Substring(text), layout); + } + } + + /// + /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . /// . /// The input text to test. /// The minimum size for that text and fot to render properly. API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text) { - return MeasureText(text, TextLayoutOptions()); + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return MeasureTextInternal(Render2D::FallbackFonts, text, TextLayoutOptions()); + } + else { + return MeasureTextInternal(text, TextLayoutOptions()); + } } /// - /// Measures minimum size of the rectangle that will be needed to draw given text + /// Measures minimum size of the rectangle that will be needed to draw given text, follows the fallback settings defined in . /// . /// The input text to test. /// The input text range (substring range of the input text parameter). /// The minimum size for that text and fot to render properly. API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange) { - return MeasureText(textRange.Substring(text), TextLayoutOptions()); + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return MeasureTextInternal(Render2D::FallbackFonts, textRange.Substring(text), TextLayoutOptions()); + } + else { + return MeasureTextInternal(textRange.Substring(text), TextLayoutOptions()); + } } /// - /// Measures minimum size of the rectangle that will be needed to draw given text. - /// - /// The input text to test. - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The layout properties. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return MeasureText(fallbacks, textRange.Substring(text), layout); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text - /// . - /// The input text to test. - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text) - { - return MeasureText(fallbacks, text, TextLayoutOptions()); - } - - /// - /// Measures minimum size of the rectangle that will be needed to draw given text - /// . - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The minimum size for that text and fot to render properly. - API_FUNCTION() FORCE_INLINE Float2 MeasureText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange) - { - return MeasureText(fallbacks, textRange.Substring(text), TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location. + /// Calculates hit character index at given location, with font fallbacking disabled. /// /// The input text to test. /// The input location to test. /// The text layout properties. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() int32 HitTestTextInternal(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Calculates hit character index at given location. + /// Calculates hit character index at given location, with font fallbacking disabled. /// /// The input text to test. /// The input text range (substring range of the input text parameter). /// The input location to test. /// The text layout properties. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) { - return HitTestText(textRange.Substring(text), location, layout); + return HitTestTextInternal(textRange.Substring(text), location, layout); } /// - /// Calculates hit character index at given location. + /// Calculates hit character index at given location, with font fallbacking disabled. + /// + /// The input text to test. + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(const StringView& text, const Float2& location) + { + return HitTestTextInternal(text, location, TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location, with font fallbacking disabled. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) + { + return HitTestTextInternal(textRange.Substring(text), location, TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location, using custom fallback options. + /// + /// The input text to test. + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Calculates hit character index at given location, using custom fallback options. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return HitTestTextInternal(fallbacks, textRange.Substring(text), location, layout); + } + + /// + /// Calculates hit character index at given location, using custom fallback options. + /// + /// The input text to test. + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, const Float2& location) + { + return HitTestTextInternal(fallbacks, text, location, TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location, using custom fallback options. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestTextInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) + { + return HitTestTextInternal(fallbacks, textRange.Substring(text), location, TextLayoutOptions()); + } + + /// + /// Calculates hit character index at given location, follows the fallback settings defined in . + /// + /// The input text to test. + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) { + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return HitTestTextInternal(Render2D::FallbackFonts, text, location, layout); + } + else { + return HitTestTextInternal(text, location, layout); + } + } + + /// + /// Calculates hit character index at given location, follows the fallback settings defined in . + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The input location to test. + /// The text layout properties. + /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). + API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) + { + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return HitTestTextInternal(Render2D::FallbackFonts, textRange.Substring(text), location, layout); + } + else { + return HitTestTextInternal(textRange.Substring(text), location, layout); + } + + } + + /// + /// Calculates hit character index at given location, follows the fallback settings defined in . /// /// The input text to test. /// The input location to test. /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, const Float2& location) { - return HitTestText(text, location, TextLayoutOptions()); + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return HitTestTextInternal(Render2D::FallbackFonts, text, location, TextLayoutOptions()); + } + else { + return HitTestTextInternal(text, location, TextLayoutOptions()); + } } /// - /// Calculates hit character index at given location. + /// Calculates hit character index at given location, follows the fallback settings defined in . /// /// The input text to test. /// The input text range (substring range of the input text parameter). @@ -637,89 +833,156 @@ public: /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). API_FUNCTION() FORCE_INLINE int32 HitTestText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) { - return HitTestText(textRange.Substring(text), location, TextLayoutOptions()); + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return HitTestTextInternal(Render2D::FallbackFonts, textRange.Substring(text), location, TextLayoutOptions()); + } + else { + return HitTestTextInternal(textRange.Substring(text), location, TextLayoutOptions()); + } } /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The text layout properties. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return HitTestText(fallbacks, textRange.Substring(text), location, layout); - } - - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, const Float2& location) - { - return HitTestText(fallbacks, text, location, TextLayoutOptions()); - } - - /// - /// Calculates hit character index at given location. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The input location to test. - /// The selected character position index (can be equal to text length if location is outside of the layout rectangle). - API_FUNCTION() FORCE_INLINE int32 HitTestText(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Float2& location) - { - return HitTestText(fallbacks, textRange.Substring(text), location, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index. + /// Calculates character position for given text and character index, with font fallbacking disabled. /// /// The input text to test. /// The text position to get coordinates of. /// The text layout properties. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + API_FUNCTION() Float2 GetCharPositionInternal(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); /// - /// Calculates character position for given text and character index. + /// Calculates character position for given text and character index, with font fallbacking disabled. /// /// The input text to test. /// The input text range (substring range of the input text parameter). /// The text position to get coordinates of. /// The text layout properties. /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) + API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) { - return GetCharPosition(textRange.Substring(text), index, layout); + return GetCharPositionInternal(textRange.Substring(text), index, layout); } /// - /// Calculates character position for given text and character index + /// Calculates character position for given text and character index, with font fallbacking disabled. + /// + /// The input text to test. + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(const StringView& text, int32 index) + { + return GetCharPositionInternal(text, index, TextLayoutOptions()); + } + + /// + /// Calculates character position for given text and character index, with font fallbacking disabled. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) + { + return GetCharPositionInternal(textRange.Substring(text), index, TextLayoutOptions()); + } + + /// + /// Calculates character position for given text and character index, using custom fallback options. + /// + /// The input text to test. + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); + + /// + /// Calculates character position for given text and character index, using custom fallback options. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) + { + return GetCharPositionInternal(fallbacks, textRange.Substring(text), index, layout); + } + + /// + /// Calculates character position for given text and character index, using custom fallback options. + /// + /// The input text to test. + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, int32 index) + { + return GetCharPositionInternal(fallbacks, text, index, TextLayoutOptions()); + } + + /// + /// Calculates character position for given text and character index, using custom fallback options. + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPositionInternal(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) + { + return GetCharPositionInternal(fallbacks, textRange.Substring(text), index, TextLayoutOptions()); + } + + /// + /// Calculates character position for given text and character index, follows the fallback settings defined in . + /// + /// The input text to test. + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) { + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return GetCharPositionInternal(Render2D::FallbackFonts, text, index, layout); + } + else { + return GetCharPositionInternal(text, index, layout); + } + } + + /// + /// Calculates character position for given text and character index, follows the fallback settings defined in . + /// + /// The input text to test. + /// The input text range (substring range of the input text parameter). + /// The text position to get coordinates of. + /// The text layout properties. + /// The character position (upper left corner which can be used for a caret position). + API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) + { + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return GetCharPositionInternal(Render2D::FallbackFonts, textRange.Substring(text), index, layout); + } + else { + return GetCharPositionInternal(textRange.Substring(text), index, layout); + } + } + + /// + /// Calculates character position for given text and character index, follows the fallback settings defined in . /// /// The input text to test. /// The text position to get coordinates of. /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, int32 index) { - return GetCharPosition(text, index, TextLayoutOptions()); + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return GetCharPositionInternal(Render2D::FallbackFonts, text, index, TextLayoutOptions()); + } + else { + return GetCharPositionInternal(text, index, TextLayoutOptions()); + } } /// - /// Calculates character position for given text and character index + /// Calculates character position for given text and character index, follows the fallback settings defined in . /// /// The input text to test. /// The input text range (substring range of the input text parameter). @@ -727,52 +990,12 @@ public: /// The character position (upper left corner which can be used for a caret position). API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) { - return GetCharPosition(textRange.Substring(text), index, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index. - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout); - - /// - /// Calculates character position for given text and character index. - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The text layout properties. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout) - { - return GetCharPosition(fallbacks, textRange.Substring(text), index, layout); - } - - /// - /// Calculates character position for given text and character index - /// - /// The input text to test. - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, int32 index) - { - return GetCharPosition(fallbacks, text, index, TextLayoutOptions()); - } - - /// - /// Calculates character position for given text and character index - /// - /// The input text to test. - /// The input text range (substring range of the input text parameter). - /// The text position to get coordinates of. - /// The character position (upper left corner which can be used for a caret position). - API_FUNCTION() FORCE_INLINE Float2 GetCharPosition(FontFallbackList* fallbacks, const StringView& text, API_PARAM(Ref) const TextRange& textRange, int32 index) - { - return GetCharPosition(fallbacks, textRange.Substring(text), index, TextLayoutOptions()); + if (Render2D::EnableFontFallback && Render2D::FallbackFonts) { + return GetCharPositionInternal(Render2D::FallbackFonts, textRange.Substring(text), index, TextLayoutOptions()); + } + else { + return GetCharPositionInternal(textRange.Substring(text), index, TextLayoutOptions()); + } } /// diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 351bb3700..fa52baff0 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -182,6 +182,8 @@ struct ClipMask }; Render2D::RenderingFeatures Render2D::Features = RenderingFeatures::VertexSnapping; +bool Render2D::EnableFontFallback = true; +FontFallbackList* Render2D::FallbackFonts = nullptr; namespace { diff --git a/Source/Engine/Render2D/Render2D.cs b/Source/Engine/Render2D/Render2D.cs index 6e4f1dc1d..b36f155df 100644 --- a/Source/Engine/Render2D/Render2D.cs +++ b/Source/Engine/Render2D/Render2D.cs @@ -102,7 +102,7 @@ namespace FlaxEngine } /// - /// Draws a text. + /// Draws a text, follows the font fallback settings defined in . /// /// The font to use. /// The text to render. @@ -128,7 +128,7 @@ namespace FlaxEngine } /// - /// Draws a text using a custom material shader. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). + /// Draws a text using a custom material shader. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling). Follows the font fallback settings defined in . /// /// The font to use. /// Custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index fa59b1d79..5813d6f87 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -54,8 +54,6 @@ API_CLASS(Static) class FLAXENGINE_API Render2D }; public: - API_FIELD() static bool EnableFontFallback; - API_FIELD() static FontFallbackList* FallbackFonts; /// /// Checks if interface is during rendering phrase (Draw calls may be performed without failing). @@ -72,6 +70,10 @@ public: /// API_FIELD() static RenderingFeatures Features; + API_FIELD() static bool EnableFontFallback; + + API_FIELD() static FontFallbackList* FallbackFonts; + /// /// Called when frame rendering begins by the graphics device. /// diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index feb34418b..02337411b 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -261,7 +261,7 @@ namespace FlaxEngine.GUI Render2D.DrawRectangle(clientRect, borderColor, BorderThickness); // Draw text - FallbackTextUtils.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); } /// diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 3b298b136..ecca2978f 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -475,7 +475,7 @@ namespace FlaxEngine.GUI var font = Font.GetFont(); for (int i = 0; i < _items.Count; i++) { - itemsWidth = Mathf.Max(itemsWidth, itemsMargin + 4 + FallbackTextUtils.MeasureText(font, _items[i]).X); + itemsWidth = Mathf.Max(itemsWidth, itemsMargin + 4 + font.MeasureText(_items[i]).X); } */ var itemsWidth = Width; @@ -673,7 +673,7 @@ namespace FlaxEngine.GUI var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); var textColor = TextColor; - FallbackTextUtils.DrawText(Font.GetFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(Font.GetFont(), FontMaterial, _items[_selectedIndex], textRect, enabled ? textColor : textColor * 0.5f, TextAlignment.Near, TextAlignment.Center); Render2D.PopClip(); } diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index 3d1caad28..3c7c04fb2 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -233,7 +233,7 @@ namespace FlaxEngine.GUI } } - FallbackTextUtils.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); if (ClipText) Render2D.PopClip(); @@ -254,7 +254,7 @@ namespace FlaxEngine.GUI layout.Bounds.Size.X = Width - Margin.Width; else if (_autoWidth && !_autoHeight) layout.Bounds.Size.Y = Height - Margin.Height; - _textSize = FallbackTextUtils.MeasureText(font, _text, ref layout); + _textSize = font.MeasureText(_text, ref layout); _textSize.Y *= BaseLinesGapScale; // Check if size is controlled via text diff --git a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs index 8d7858af0..46f0fb1ad 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs @@ -154,7 +154,7 @@ namespace FlaxEngine.GUI if (!font) break; height = font.Height / DpiScale; - return textBlock.Bounds.Location + FallbackTextUtils.GetCharPosition(font, _text, ref textBlock.Range, index - textBlock.Range.StartIndex); + return textBlock.Bounds.Location + font.GetCharPosition(_text, ref textBlock.Range, index - textBlock.Range.StartIndex); } } @@ -196,7 +196,7 @@ namespace FlaxEngine.GUI var font = textBlock.Style.Font.GetFont(); if (!font && textBlock.Range.Length > 0) break; - return FallbackTextUtils.HitTestText(font, _text, ref textBlock.Range, location - textBlock.Bounds.Location) + textBlock.Range.StartIndex; + return font.HitTestText(_text, ref textBlock.Range, location - textBlock.Bounds.Location) + textBlock.Range.StartIndex; } } @@ -288,8 +288,8 @@ namespace FlaxEngine.GUI // Selection if (hasSelection && textBlock.Style.BackgroundSelectedBrush != null && textBlock.Range.Intersect(ref selection)) { - var leftEdge = selection.StartIndex <= textBlock.Range.StartIndex ? textBlock.Bounds.UpperLeft : FallbackTextUtils.GetCharPosition(font, _text, selection.StartIndex); - var rightEdge = selection.EndIndex >= textBlock.Range.EndIndex ? textBlock.Bounds.UpperRight : FallbackTextUtils.GetCharPosition(font, _text, selection.EndIndex); + var leftEdge = selection.StartIndex <= textBlock.Range.StartIndex ? textBlock.Bounds.UpperLeft : font.GetCharPosition(_text, selection.StartIndex); + var rightEdge = selection.EndIndex >= textBlock.Range.EndIndex ? textBlock.Bounds.UpperRight : font.GetCharPosition(_text, selection.EndIndex); float height = font.Height / DpiScale; float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); alpha *= alpha; diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index b1861df56..3c0d55008 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -104,7 +104,7 @@ namespace FlaxEngine.GUI return Float2.Zero; } - return FallbackTextUtils.MeasureText(font, _text, ref _layout); + return font.MeasureText(_text, ref _layout); } /// @@ -117,8 +117,8 @@ namespace FlaxEngine.GUI return Float2.Zero; } - height = FallbackTextUtils.GetMaxHeight(font) / DpiScale; - return FallbackTextUtils.GetCharPosition(font, _text, index, ref _layout); + height = font.GetMaxHeight() / DpiScale; + return font.GetCharPosition(_text, index, ref _layout); } /// @@ -130,7 +130,7 @@ namespace FlaxEngine.GUI return 0; } - return FallbackTextUtils.HitTestText(font, _text, location, ref _layout); + return font.HitTestText(_text, location, ref _layout); } /// @@ -169,9 +169,9 @@ namespace FlaxEngine.GUI // Check if sth is selected to draw selection if (HasSelection) { - var leftEdge = FallbackTextUtils.GetCharPosition(font, _text, SelectionLeft, ref _layout); - var rightEdge = FallbackTextUtils.GetCharPosition(font, _text, SelectionRight, ref _layout); - float fontHeight = FallbackTextUtils.GetMaxHeight(font) / DpiScale; + var leftEdge = font.GetCharPosition(_text, SelectionLeft, ref _layout); + var rightEdge = font.GetCharPosition(_text, SelectionRight, ref _layout); + float fontHeight = font.GetMaxHeight() / DpiScale; // Draw selection background float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); @@ -211,11 +211,11 @@ namespace FlaxEngine.GUI var color = TextColor; if (!enabled) color *= 0.6f; - FallbackTextUtils.DrawText(font, _text, color, ref _layout, TextMaterial); + Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); } else if (!string.IsNullOrEmpty(_watermarkText) && !IsFocused) { - FallbackTextUtils.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); } // Caret diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index 123e0f034..66e7413eb 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -374,7 +374,7 @@ namespace FlaxEngine.GUI textColor *= 0.6f; } - FallbackTextUtils.DrawText(HeaderTextFont.GetFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); + Render2D.DrawText(HeaderTextFont.GetFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); if (!_isClosed && EnableContainmentLines) { diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index f4fca13eb..6092f0f72 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -69,12 +69,6 @@ namespace FlaxEngine.GUI set => _fontSmall = new FontReference(value); } - /// - /// The fallback fonts to use if the primary font can't render the char. - /// - [EditorOrder(50)] - public FallbackFonts Fallbacks; - /// /// The background color. /// diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index 41d06b017..8d35c21b9 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -234,7 +234,7 @@ namespace FlaxEngine.GUI Render2D.FillRectangle(new Rectangle(1.1f, 1.1f, Width - 2, Height - 2), style.Background); // Tooltip text - FallbackTextUtils.DrawText( + Render2D.DrawText( style.FontMedium, _currentText, GetClientArea(), From 6ab1663a1429591d0678bab1790e3e0b4a9628ca Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Sun, 3 Dec 2023 15:18:27 +0800 Subject: [PATCH 14/16] Add missing xml annotations --- Source/Engine/Core/Config/GraphicsSettings.h | 4 +-- Source/Engine/Render2D/Render2D.h | 26 ++++++++++++++++++++ Source/Engine/UI/GUI/Style.cs | 1 - Source/Engine/UI/GUI/Tooltip.cs | 1 - 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Core/Config/GraphicsSettings.h b/Source/Engine/Core/Config/GraphicsSettings.h index 7abfbb048..e109a55e4 100644 --- a/Source/Engine/Core/Config/GraphicsSettings.h +++ b/Source/Engine/Core/Config/GraphicsSettings.h @@ -121,13 +121,13 @@ public: PostProcessSettings PostProcessSettings; /// - /// + /// Whether to enable font fallbacking globally. /// API_FIELD(Attributes = "EditorOrder(12000), EditorDisplay(\"Text Render Settings\", EditorDisplayAttribute.InlineStyle)") bool EnableFontFallback = true; /// - /// + /// The fallback fonts used for text rendering, ignored if null. /// API_FIELD(Attributes = "EditorOrder(12005), EditorDisplay(\"Text Render Settings\", EditorDisplayAttribute.InlineStyle)") FontFallbackList* FallbackFonts; diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index 5813d6f87..6657d8542 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -281,6 +281,15 @@ public: } } + /// + /// Draws a text, follows the fallback settings defined in . + /// + /// The font to use. + /// The text to render. + /// The input text range (substring range of the input text parameter). + /// The text color. + /// The text location. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr) { if (EnableFontFallback && FallbackFonts) { DrawTextInternal(font, FallbackFonts, text, textRange, color, location, customMaterial); @@ -290,6 +299,14 @@ public: } } + /// + /// Draws a text with formatting, follows the fallback settings defined in . + /// + /// The font to use. + /// The text to render. + /// The text color. + /// The text layout properties. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr) { if (EnableFontFallback && FallbackFonts) { DrawTextInternal(font, FallbackFonts, text, color, layout, customMaterial); @@ -299,6 +316,15 @@ public: } } + /// + /// Draws a text with formatting, follows the fallback settings defined in . + /// + /// The font to use. + /// The text to render. + /// The input text range (substring range of the input text parameter). + /// The text color. + /// The text layout properties. + /// The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture. API_FUNCTION() FORCE_INLINE static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr) { if (EnableFontFallback && FallbackFonts) { DrawTextInternal(font, FallbackFonts, text, textRange, color, layout, customMaterial); diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index 6092f0f72..22b8f52af 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -1,6 +1,5 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. - namespace FlaxEngine.GUI { /// diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index 8d35c21b9..734fb078f 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -243,7 +243,6 @@ namespace FlaxEngine.GUI TextAlignment.Center, TextWrapping.WrapWords ); - } /// From 360c75355c014bdb7be5051ea41460a777781ba4 Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Sun, 3 Dec 2023 16:04:50 +0800 Subject: [PATCH 15/16] Fix build error under non-windows platforms --- Source/Engine/Render2D/Font.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index 9f28aac01..9a2037b3f 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -103,7 +103,7 @@ void Font::Invalidate() _characters.Clear(); } -inline API_FUNCTION() float Font::GetMaxHeight(FontFallbackList* fallbacks) const +float Font::GetMaxHeight(FontFallbackList* fallbacks) const { float height = GetHeight(); auto& fallbackFonts = fallbacks->GetFontList(GetSize()); From 4497b2ca7d5ee86f67d8ae19cd1db3220fe1464a Mon Sep 17 00:00:00 2001 From: ExMatics HydrogenC <33123710+HydrogenC@users.noreply.github.com> Date: Tue, 19 Dec 2023 12:36:12 +0800 Subject: [PATCH 16/16] Add fallback settings for control --- Source/Engine/UI/GUI/Common/Label.cs | 27 ++++++++++++++++-- Source/Engine/UI/GUI/Common/TextBox.cs | 39 ++++++++++++++++++++------ 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index 3c7c04fb2..8de54fda3 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -182,6 +182,12 @@ namespace FlaxEngine.GUI set => _autoFitTextRange = value; } + /// + /// Gets or sets whether to fallback when the primary font cannot render a char. + /// + [EditorOrder(120), DefaultValue(true), Tooltip("Whether to fallback when the font cannot render a char.")] + public bool EnableFontFallback { get; set; } = true; + /// /// Initializes a new instance of the class. /// @@ -233,7 +239,23 @@ namespace FlaxEngine.GUI } } - Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + if (EnableFontFallback) + { + Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale); + } + else + { + var layout = new TextLayoutOptions + { + Bounds = rect, + HorizontalAlignment = hAlignment, + VerticalAlignment = wAlignment, + TextWrapping = Wrapping, + Scale = scale, + BaseLinesGapScale = BaseLinesGapScale, + }; + Render2D.DrawTextInternal(_font.GetFont(), _text, color, ref layout, Material); + } if (ClipText) Render2D.PopClip(); @@ -254,7 +276,8 @@ namespace FlaxEngine.GUI layout.Bounds.Size.X = Width - Margin.Width; else if (_autoWidth && !_autoHeight) layout.Bounds.Size.Y = Height - Margin.Height; - _textSize = font.MeasureText(_text, ref layout); + _textSize = EnableFontFallback ? + font.MeasureText(_text, ref layout) : font.MeasureTextInternal(_text, ref layout); _textSize.Y *= BaseLinesGapScale; // Check if size is controlled via text diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index 3c0d55008..53266a1b2 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -1,6 +1,8 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System.ComponentModel; + namespace FlaxEngine.GUI { /// @@ -65,6 +67,12 @@ namespace FlaxEngine.GUI [EditorDisplay("Text Style"), EditorOrder(2022), Tooltip("The color of the selection (Transparent if not used).")] public Color SelectionColor { get; set; } + /// + /// Gets or sets whether to fallback when the primary font cannot render a char. + /// + [EditorOrder(120), DefaultValue(true), Tooltip("Whether to fallback when the font cannot render a char.")] + public bool EnableFontFallback { get; set; } = true; + /// /// Initializes a new instance of the class. /// @@ -104,7 +112,8 @@ namespace FlaxEngine.GUI return Float2.Zero; } - return font.MeasureText(_text, ref _layout); + return EnableFontFallback ? font.MeasureText(_text, ref _layout) : + font.MeasureTextInternal(_text, ref _layout); } /// @@ -117,8 +126,9 @@ namespace FlaxEngine.GUI return Float2.Zero; } - height = font.GetMaxHeight() / DpiScale; - return font.GetCharPosition(_text, index, ref _layout); + height = (EnableFontFallback ? font.GetMaxHeight() : font.Height) / DpiScale; + return EnableFontFallback ? font.GetCharPosition(_text, index, ref _layout) : + font.GetCharPositionInternal(_text, index, ref _layout); } /// @@ -130,7 +140,8 @@ namespace FlaxEngine.GUI return 0; } - return font.HitTestText(_text, location, ref _layout); + return EnableFontFallback ? font.HitTestText(_text, location, ref _layout) : + font.HitTestTextInternal(_text, location, ref _layout); } /// @@ -169,8 +180,12 @@ namespace FlaxEngine.GUI // Check if sth is selected to draw selection if (HasSelection) { - var leftEdge = font.GetCharPosition(_text, SelectionLeft, ref _layout); - var rightEdge = font.GetCharPosition(_text, SelectionRight, ref _layout); + var leftEdge = EnableFontFallback ? + font.GetCharPosition(_text, SelectionLeft, ref _layout) : + font.GetCharPositionInternal(_text, SelectionLeft, ref _layout); + var rightEdge = EnableFontFallback ? + font.GetCharPosition(_text, SelectionRight, ref _layout) : + font.GetCharPositionInternal(_text, SelectionRight, ref _layout); float fontHeight = font.GetMaxHeight() / DpiScale; // Draw selection background @@ -211,11 +226,19 @@ namespace FlaxEngine.GUI var color = TextColor; if (!enabled) color *= 0.6f; - Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); + if (EnableFontFallback) + Render2D.DrawText(font, _text, color, ref _layout, TextMaterial); + else + // Draw without fallback + Render2D.DrawTextInternal(font, _text, color, ref _layout, TextMaterial); } else if (!string.IsNullOrEmpty(_watermarkText) && !IsFocused) { - Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + if (EnableFontFallback) + Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + else + // Draw without fallback + Render2D.DrawTextInternal(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); } // Caret