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];