Add font fallback

Note: All the `First()` in the code are temperary workarounds to make it work and require refractoring
This commit is contained in:
ExMatics HydrogenC
2023-11-28 07:17:46 +08:00
parent b3a18883ca
commit 47a25c7828
47 changed files with 787 additions and 87 deletions

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.GUI;
using FlaxEditor.GUI.Drag;
using FlaxEditor.GUI.Tree;
@@ -140,8 +141,8 @@ namespace FlaxEditor.Content
var textRect = TextRect;
for (int i = 0; i < ranges.Length; i++)
{
var start = font.GetCharPosition(text, ranges[i].StartIndex);
var end = font.GetCharPosition(text, ranges[i].EndIndex);
var start = font.First().GetCharPosition(text, ranges[i].StartIndex);
var end = font.First().GetCharPosition(text, ranges[i].EndIndex);
_highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height));
}
isThisVisible = true;

View File

@@ -3,6 +3,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.Actions;
using FlaxEditor.Content;
using FlaxEditor.GUI;
@@ -42,7 +43,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
// Add script button
var buttonText = "Add script";
var textSize = Style.Current.FontMedium.MeasureText(buttonText);
var textSize = Style.Current.FontMedium.First().MeasureText(buttonText);
float addScriptButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4;
var buttonHeight = (textSize.Y < 18) ? 18 : textSize.Y + 4;
_addScriptsButton = new Button

View File

@@ -247,7 +247,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
// Info
var info = new Label(0, title.Bottom, DialogWidth, InfoHeight)
{
Font = new FontReference(style.FontSmall),
Font = new FontReference(style.FontSmall.First()),
Text = "Shift: also set bounds\nControl: also set pivot",
Parent = this
};
@@ -423,7 +423,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
// Set control type button
var space = layout.Space(20);
var buttonText = "Set Type";
var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText);
var textSize = FlaxEngine.GUI.Style.Current.FontMedium.First().MeasureText(buttonText);
float setTypeButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4;
var setTypeButton = new Button
{

View File

@@ -2,6 +2,7 @@
using FlaxEngine;
using FlaxEngine.GUI;
using System.Linq;
namespace FlaxEditor.CustomEditors.Editors
{
@@ -100,7 +101,7 @@ namespace FlaxEditor.CustomEditors.Editors
_linkButton.Clicked += ToggleLink;
ToggleEnabled();
SetLinkStyle();
var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(LinkedLabel.Text.Value);
var textSize = FlaxEngine.GUI.Style.Current.FontMedium.First().MeasureText(LinkedLabel.Text.Value);
_linkButton.LocalX += textSize.X + 10;
LinkedLabel.SetupContextMenu += (label, menu, editor) =>
{

View File

@@ -631,7 +631,7 @@ namespace FlaxEditor.CustomEditors.Editors
TooltipText = "Edit...",
Parent = _label,
};
var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText);
var textSize = FlaxEngine.GUI.Style.Current.FontMedium.First().MeasureText(buttonText);
if (textSize.Y > button.Width)
button.Width = textSize.Y + 2;

View File

@@ -54,6 +54,8 @@ namespace FlaxEditor
/// </summary>
public static string PrimaryFont = "Editor/Fonts/Roboto-Regular";
public static string CJKFont = "Editor/Fonts/NotoSansSC-Medium";
/// <summary>
/// The Inconsolata Regular font.
/// </summary>

View File

@@ -273,7 +273,7 @@ namespace FlaxEditor.GUI
MaximumItemsInViewCount = 20;
var style = Style.Current;
Font = new FontReference(style.FontMedium);
Font = new FontReference(style.FontMedium.First());
TextColor = style.Foreground;
BackgroundColor = style.BackgroundNormal;
BackgroundColorHighlighted = BackgroundColor;

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Linq;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -233,11 +234,11 @@ namespace FlaxEditor.GUI.ContextMenu
{
var style = Style.Current;
float width = 20;
if (style.FontMedium)
if (style.FontMedium.First())
{
width += style.FontMedium.MeasureText(Text).X;
width += style.FontMedium.First().MeasureText(Text).X;
if (!string.IsNullOrEmpty(ShortKeys))
width += 40 + style.FontMedium.MeasureText(ShortKeys).X;
width += 40 + style.FontMedium.First().MeasureText(ShortKeys).X;
}
return Mathf.Max(width, base.MinimumWidth);

View File

@@ -437,7 +437,7 @@ namespace FlaxEditor.GUI
_contentsColor = style.Background.RGBMultiplied(0.7f);
_linesColor = style.ForegroundDisabled.RGBMultiplied(0.7f);
_labelsColor = style.ForegroundDisabled;
_labelsFont = style.FontSmall;
_labelsFont = style.FontSmall.First();
_mainPanel = new Panel(ScrollBars.Both)
{

View File

@@ -6,6 +6,7 @@ using FlaxEngine;
using FlaxEngine.Assertions;
using FlaxEngine.GUI;
using FlaxEditor.Options;
using System.Linq;
namespace FlaxEditor.GUI.Docking
{
@@ -488,7 +489,7 @@ namespace FlaxEditor.GUI.Docking
{
var style = Style.Current;
if (style?.FontMedium != null)
_titleSize = style.FontMedium.MeasureText(_title);
_titleSize = style.FontMedium.First().MeasureText(_title);
}
base.PerformLayoutBeforeChildren();

View File

@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Input;
using FlaxEditor.Utilities;
@@ -86,8 +87,8 @@ namespace FlaxEditor.GUI
var font = style.FontSmall;
for (int i = 0; i < ranges.Length; i++)
{
var start = font.GetCharPosition(Name, ranges[i].StartIndex);
var end = font.GetCharPosition(Name, ranges[i].EndIndex);
var start = font.First().GetCharPosition(Name, ranges[i].StartIndex);
var end = font.First().GetCharPosition(Name, ranges[i].EndIndex);
_highlights.Add(new Rectangle(start.X + 2, 0, end.X - start.X, Height));
}
Visible = true;

View File

@@ -2,6 +2,7 @@
using FlaxEngine;
using FlaxEngine.GUI;
using System.Linq;
namespace FlaxEditor.GUI
{
@@ -101,8 +102,8 @@ namespace FlaxEditor.GUI
var style = Style.Current;
float width = 18;
if (style.FontMedium)
width += style.FontMedium.MeasureText(Text).X;
if (style.FontMedium.First())
width += style.FontMedium.First().MeasureText(Text).X;
Width = width;
}

View File

@@ -2,6 +2,7 @@
using FlaxEngine;
using FlaxEngine.GUI;
using System.Linq;
namespace FlaxEditor.GUI
{
@@ -65,9 +66,9 @@ namespace FlaxEditor.GUI
{
var style = Style.Current;
if (style.FontMedium)
if (style.FontMedium.First())
{
Width = style.FontMedium.MeasureText(Text).X + 2 * DefaultMargin;
Width = style.FontMedium.First().MeasureText(Text).X + 2 * DefaultMargin;
}
}
}

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Linq;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -38,8 +39,8 @@ namespace FlaxEditor.GUI
{
Depth = -1;
if (Height < Style.Current.FontMedium.Height)
Height = Style.Current.FontMedium.Height + 4;
if (Height < Style.Current.FontMedium.First().Height)
Height = Style.Current.FontMedium.First().Height + 4;
}
/// <inheritdoc />

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -129,7 +130,7 @@ namespace FlaxEditor.GUI
Render2D.FillRectangle(rect, column.TitleBackgroundColor);
var style = Style.Current;
var font = column.TitleFont ?? style.FontMedium;
var font = column.TitleFont ?? style.FontMedium.First();
Render2D.DrawText(font, column.Title, rect, column.TitleColor, TextAlignment.Center, TextAlignment.Center);
if (columnIndex < _columns.Length - 1)

View File

@@ -345,7 +345,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
if (_previewValue != null)
{
// Based on Track.Draw for track text placement
var left = _xOffset + 16 + Style.Current.FontSmall.MeasureText(Title ?? Name).X;
var left = _xOffset + 16 + Style.Current.FontSmall.First().MeasureText(Title ?? Name).X;
if (Icon.IsValid)
left += 18;
if (IsExpanded)

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Linq;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -150,8 +151,8 @@ namespace FlaxEditor.GUI
if (hasSprite)
width += iconSize;
if (!string.IsNullOrEmpty(_text) && style.FontMedium)
width += style.FontMedium.MeasureText(_text).X + (hasSprite ? DefaultMargin : 0);
if (!string.IsNullOrEmpty(_text) && style.FontMedium.First())
width += style.FontMedium.First().MeasureText(_text).X + (hasSprite ? DefaultMargin : 0);
Width = width;
}

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Linq;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -317,7 +318,7 @@ namespace FlaxEditor.GUI.Tree
BackgroundColorSelected = style.BackgroundSelected;
BackgroundColorHighlighted = style.BackgroundHighlighted;
BackgroundColorSelectedUnfocused = style.LightBackground;
TextFont = new FontReference(style.FontSmall);
TextFont = new FontReference(style.FontSmall.First());
}
/// <summary>

View File

@@ -237,12 +237,18 @@ namespace FlaxEditor.Options
public int NumberOfGameClientsToLaunch = 1;
private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.PrimaryFont);
private static FontAsset _cjkFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(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<FontAsset>(EditorAssets.InconsolataRegularFont), 10);
public FontReference CJKFont
{
get => new FontReference(_cjkFont, 9);
}
/// <summary>
/// Gets or sets the title font for editor UI.
/// </summary>

View File

@@ -261,8 +261,9 @@ namespace FlaxEditor.Options
// Fonts
FontTitle = options.Interface.TitleFont.GetFont(),
FontLarge = options.Interface.LargeFont.GetFont(),
FontMedium = options.Interface.MediumFont.GetFont(),
FontSmall = options.Interface.SmallFont.GetFont(),
FontMedium = new Font[] { options.Interface.MediumFont.GetFont(), options.Interface.CJKFont.GetFont() },
FontSmall = new Font[] { options.Interface.SmallFont.GetFont(), options.Interface.CJKFont.GetFont() },
FontCJK = options.Interface.CJKFont.GetFont(),
// Icons
ArrowDown = Editor.Icons.ArrowDown12,
@@ -314,8 +315,8 @@ namespace FlaxEditor.Options
// Fonts
FontTitle = options.Interface.TitleFont.GetFont(),
FontLarge = options.Interface.LargeFont.GetFont(),
FontMedium = options.Interface.MediumFont.GetFont(),
FontSmall = options.Interface.SmallFont.GetFont(),
FontMedium = new Font[] { options.Interface.MediumFont.GetFont(), options.Interface.CJKFont.GetFont() },
FontSmall = new Font[] { options.Interface.SmallFont.GetFont(), options.Interface.CJKFont.GetFont() },
// Icons
ArrowDown = Editor.Icons.ArrowDown12,

View File

@@ -142,8 +142,8 @@ namespace FlaxEditor.SceneGraph.GUI
var textRect = TextRect;
for (int i = 0; i < ranges.Length; i++)
{
var start = font.GetCharPosition(text, ranges[i].StartIndex);
var end = font.GetCharPosition(text, ranges[i].EndIndex);
var start = font.First().GetCharPosition(text, ranges[i].StartIndex);
var end = font.First().GetCharPosition(text, ranges[i].EndIndex);
_highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height));
}
isThisVisible = true;

View File

@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.CustomEditors.Dedicated;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Drag;
@@ -100,7 +101,7 @@ namespace FlaxEditor.Surface.Archetypes
_debugRelevant = Behavior.GetNodeDebugRelevancy(instance, behavior);
_debugInfo = Behavior.GetNodeDebugInfo(instance, behavior);
if (!string.IsNullOrEmpty(_debugInfo))
_debugInfoSize = Style.Current.FontSmall.MeasureText(_debugInfo);
_debugInfoSize = Style.Current.FontSmall.First().MeasureText(_debugInfo);
}
}

View File

@@ -200,8 +200,8 @@ namespace FlaxEditor.Surface.ContextMenu
var font = style.FontSmall;
for (int i = 0; i < ranges.Length; i++)
{
var start = font.GetCharPosition(_archetype.Title, ranges[i].StartIndex);
var end = font.GetCharPosition(_archetype.Title, ranges[i].EndIndex);
var start = font.First().GetCharPosition(_archetype.Title, ranges[i].StartIndex);
var end = font.First().GetCharPosition(_archetype.Title, ranges[i].EndIndex);
_highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height));
if (ranges[i].StartIndex <= 0)
@@ -222,8 +222,8 @@ namespace FlaxEditor.Surface.ContextMenu
_highlights.Clear();
var style = Style.Current;
var font = style.FontSmall;
var start = font.GetCharPosition(_archetype.Title, 0);
var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1);
var start = font.First().GetCharPosition(_archetype.Title, 0);
var end = font.First().GetCharPosition(_archetype.Title, _archetype.Title.Length - 1);
_highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height));
_isFullMatch = true;
Visible = true;
@@ -237,8 +237,8 @@ namespace FlaxEditor.Surface.ContextMenu
_highlights.Clear();
var style = Style.Current;
var font = style.FontSmall;
var start = font.GetCharPosition(_archetype.Title, 0);
var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1);
var start = font.First().GetCharPosition(_archetype.Title, 0);
var end = font.First().GetCharPosition(_archetype.Title, _archetype.Title.Length - 1);
_highlights.Add(new Rectangle(start.X + textRect.X, 0, end.X - start.X, Height));
Visible = true;
@@ -286,7 +286,7 @@ namespace FlaxEditor.Surface.ContextMenu
Render2D.DrawText(style.FontSmall, _archetype.Title, textRect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center);
if (_archetype.SubTitle != null)
{
var titleLength = style.FontSmall.MeasureText(_archetype.Title).X;
var titleLength = style.FontSmall.First().MeasureText(_archetype.Title).X;
var subTitleRect = new Rectangle(textRect.X + titleLength, textRect.Y, textRect.Width - titleLength, textRect.Height);
Render2D.DrawText(style.FontSmall, _archetype.SubTitle, subTitleRect, style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center);
}

View File

@@ -1428,7 +1428,7 @@ namespace FlaxEditor.Surface.Elements
if (_defaultValueEditor != null)
{
_defaultValueEditor.Location = new Float2(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y);
_defaultValueEditor.Location = new Float2(X + Width + 8 + Style.Current.FontSmall.First().MeasureText(Text).X, Y);
}
}
@@ -1635,7 +1635,7 @@ namespace FlaxEditor.Surface.Elements
{
if (DefaultValueEditors[i].CanUse(this, ref _currentType))
{
var bounds = new Rectangle(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y, 90, Height);
var bounds = new Rectangle(X + Width + 8 + Style.Current.FontSmall.First().MeasureText(Text).X, Y, 90, Height);
_editor = DefaultValueEditors[i];
try
{

View File

@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.Scripting;
using FlaxEditor.Surface.Elements;
using FlaxEditor.Surface.Undo;
@@ -199,7 +200,7 @@ namespace FlaxEditor.Surface
continue;
if (child is InputBox inputBox)
{
var boxWidth = boxLabelFont.MeasureText(inputBox.Text).X + 20;
var boxWidth = boxLabelFont.First().MeasureText(inputBox.Text).X + 20;
if (inputBox.DefaultValueEditor != null)
boxWidth += inputBox.DefaultValueEditor.Width + 4;
leftWidth = Mathf.Max(leftWidth, boxWidth);
@@ -207,7 +208,7 @@ namespace FlaxEditor.Surface
}
else if (child is OutputBox outputBox)
{
rightWidth = Mathf.Max(rightWidth, boxLabelFont.MeasureText(outputBox.Text).X + 20);
rightWidth = Mathf.Max(rightWidth, boxLabelFont.First().MeasureText(outputBox.Text).X + 20);
rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f);
}
else if (child is Control control)

View File

@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.GUI.Tabs;
using FlaxEditor.Modules;
using FlaxEditor.SceneGraph.Actors;
@@ -147,7 +148,7 @@ namespace FlaxEditor.Tools.Foliage
Parent = _noFoliagePanel,
Enabled = false
};
var textSize = Style.Current.FontMedium.MeasureText(buttonText);
var textSize = Style.Current.FontMedium.First().MeasureText(buttonText);
if (_createNewFoliage.Width < textSize.X)
{
_createNewFoliage.LocalX -= (textSize.X - _createNewFoliage.Width) / 2;

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Linq;
using FlaxEditor.GUI.Tabs;
using FlaxEditor.Modules;
using FlaxEditor.SceneGraph.Actors;
@@ -105,7 +106,7 @@ namespace FlaxEditor.Tools.Terrain
Parent = _noTerrainPanel,
Enabled = false
};
var textSize = Style.Current.FontMedium.MeasureText(buttonText);
var textSize = Style.Current.FontMedium.First().MeasureText(buttonText);
if (_createTerrainButton.Width < textSize.X)
{
_createTerrainButton.LocalX -= (textSize.X - _createTerrainButton.Width) / 2;

View File

@@ -548,9 +548,9 @@ namespace FlaxEditor.Viewport
#region Camera settings widget
var largestText = "Relative Panning";
var textSize = Style.Current.FontMedium.MeasureText(largestText);
var textSize = Style.Current.FontMedium.First().MeasureText(largestText);
var xLocationForExtras = textSize.X + 5;
var cameraSpeedTextWidth = Style.Current.FontMedium.MeasureText("0.00").X;
var cameraSpeedTextWidth = Style.Current.FontMedium.First().MeasureText("0.00").X;
// Camera Settings Widget
_cameraWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
@@ -801,7 +801,7 @@ namespace FlaxEditor.Viewport
#region View mode widget
largestText = "Brightness";
textSize = Style.Current.FontMedium.MeasureText(largestText);
textSize = Style.Current.FontMedium.First().MeasureText(largestText);
xLocationForExtras = textSize.X + 5;
var viewMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft);

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Linq;
using FlaxEditor.GUI.ContextMenu;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -162,8 +163,8 @@ namespace FlaxEditor.Viewport.Widgets
{
var style = Style.Current;
if (style != null && style.FontMedium)
Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.MeasureText(_text).X, Icon.IsValid);
if (style != null && style.FontMedium.First())
Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.First().MeasureText(_text).X, Icon.IsValid);
}
}
}

View File

@@ -2,6 +2,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.GUI.Dialogs;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -53,7 +54,7 @@ namespace FlaxEditor.Windows
Parent = this
};
var buttonText = "Copy version info";
var fontSize = Style.Current.FontMedium.MeasureText(buttonText);
var fontSize = Style.Current.FontMedium.First().MeasureText(buttonText);
var copyVersionButton = new Button(Width - fontSize.X - 8, 6, fontSize.X + 4, 20)
{
Text = buttonText,

View File

@@ -2,6 +2,7 @@
using FlaxEngine;
using FlaxEngine.GUI;
using System.Linq;
namespace FlaxEditor.Windows.Profiler
{
@@ -84,8 +85,8 @@ namespace FlaxEditor.Windows.Profiler
Render2D.FillRectangle(bounds, color);
Render2D.DrawRectangle(bounds, color * 0.5f);
if (_nameLength < 0 && style.FontMedium)
_nameLength = style.FontMedium.MeasureText(_name).X;
if (_nameLength < 0 && style.FontMedium.First())
_nameLength = style.FontMedium.First().MeasureText(_name).X;
if (_nameLength < bounds.Width + 4)
{

View File

@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.GUI.Input;
using FlaxEditor.GUI.Tabs;
using FlaxEditor.GUI.Tree;
@@ -272,8 +273,8 @@ namespace FlaxEditor.Windows
var textRect = item.TextRect;
for (int i = 0; i < ranges.Length; i++)
{
var start = font.GetCharPosition(text, ranges[i].StartIndex);
var end = font.GetCharPosition(text, ranges[i].EndIndex);
var start = font.First().GetCharPosition(text, ranges[i].StartIndex);
var end = font.First().GetCharPosition(text, ranges[i].EndIndex);
highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height));
}
item.SetHighlights(highlights);

View File

@@ -283,6 +283,206 @@ void Font::ProcessText(const StringView& text, Array<FontLineCache>& outputLines
}
}
void Font::ProcessText(const Array<Font*>& fonts, const StringView& text, Array<FontLineCache>& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout)
{
float cursorX = 0;
int32 kerning;
FontLineCache tmpLine;
FontCharacterEntry entry;
FontCharacterEntry previous;
int32 textLength = text.Length();
float scale = layout.Scale / FontManager::FontScale;
float boundsWidth = layout.Bounds.GetWidth();
float baseLinesDistanceScale = layout.BaseLinesGapScale * scale;
tmpLine.Location = Float2::Zero;
tmpLine.Size = Float2::Zero;
tmpLine.FirstCharIndex = 0;
tmpLine.LastCharIndex = -1;
int32 lastWrapCharIndex = INVALID_INDEX;
float lastWrapCharX = 0;
bool lastMoveLine = false;
int32 previousFontIndex = -1;
// The maximum font height of the current line
float maxHeight = 0;
// Process each character to split text into single lines
for (int32 currentIndex = 0; currentIndex < textLength;)
{
bool moveLine = false;
float xAdvance = 0;
int32 nextCharIndex = currentIndex + 1;
// Cache current character
const Char currentChar = text[currentIndex];
const bool isWhitespace = StringUtils::IsWhitespace(currentChar);
// Check if character can wrap words
const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar);
if (isWrapChar && currentIndex != 0)
{
lastWrapCharIndex = currentIndex;
lastWrapCharX = cursorX;
}
// Check if it's a newline character
if (currentChar == '\n')
{
// Break line
moveLine = true;
currentIndex++;
tmpLine.LastCharIndex++;
}
else
{
// Get character entry
int32 fontIndex = 0;
while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(currentChar))
{
fontIndex++;
}
// If no font can match the char, then use the first font
if (fontIndex == fonts.Count()) {
fontIndex = 0;
}
// Get character entry
fonts[fontIndex]->GetCharacter(currentChar, entry);
maxHeight = Math::Max(maxHeight, static_cast<float>(fonts[fontIndex]->GetHeight()));
// Get kerning, only when the font hasn't changed
if (!isWhitespace && previous.IsValid && previousFontIndex == fontIndex)
{
kerning = fonts[fontIndex]->GetKerning(previous.Character, entry.Character);
}
else
{
kerning = 0;
}
previous = entry;
previousFontIndex = fontIndex;
xAdvance = (kerning + entry.AdvanceX) * scale;
// Check if character fits the line or skip wrapping
if (cursorX + xAdvance <= boundsWidth || layout.TextWrapping == TextWrapping::NoWrap)
{
// Move character
cursorX += xAdvance;
tmpLine.LastCharIndex++;
}
else if (layout.TextWrapping == TextWrapping::WrapWords)
{
if (lastWrapCharIndex != INVALID_INDEX)
{
// Skip moving twice for the same character
int32 lastLineLasCharIndex = outputLines.HasItems() ? outputLines.Last().LastCharIndex : -10000;
if (lastLineLasCharIndex == lastWrapCharIndex || lastLineLasCharIndex == lastWrapCharIndex - 1 || lastLineLasCharIndex == lastWrapCharIndex - 2)
{
currentIndex = nextCharIndex;
lastMoveLine = moveLine;
continue;
}
// Move line
const Char wrapChar = text[lastWrapCharIndex];
moveLine = true;
cursorX = lastWrapCharX;
if (StringUtils::IsWhitespace(wrapChar))
{
// Skip whitespaces
tmpLine.LastCharIndex = lastWrapCharIndex - 1;
nextCharIndex = currentIndex = lastWrapCharIndex + 1;
}
else
{
tmpLine.LastCharIndex = lastWrapCharIndex - 1;
nextCharIndex = currentIndex = lastWrapCharIndex;
}
}
}
else if (layout.TextWrapping == TextWrapping::WrapChars)
{
// Move line
moveLine = true;
nextCharIndex = currentIndex;
// Skip moving twice for the same character
if (lastMoveLine)
break;
}
}
// Check if move to another line
if (moveLine)
{
// Add line
tmpLine.Size.X = cursorX;
tmpLine.Size.Y = baseLinesDistanceScale * maxHeight;
tmpLine.LastCharIndex = Math::Max(tmpLine.LastCharIndex, tmpLine.FirstCharIndex);
outputLines.Add(tmpLine);
// Reset line
tmpLine.Location.Y += baseLinesDistanceScale * maxHeight;
tmpLine.FirstCharIndex = currentIndex;
tmpLine.LastCharIndex = currentIndex - 1;
cursorX = 0;
lastWrapCharIndex = INVALID_INDEX;
lastWrapCharX = 0;
previous.IsValid = false;
// Reset max font height
maxHeight = 0;
}
currentIndex = nextCharIndex;
lastMoveLine = moveLine;
}
if (textLength != 0 && (tmpLine.LastCharIndex >= tmpLine.FirstCharIndex || text[textLength - 1] == '\n'))
{
// Add line
tmpLine.Size.X = cursorX;
tmpLine.Size.Y = baseLinesDistanceScale * maxHeight;
tmpLine.LastCharIndex = textLength - 1;
outputLines.Add(tmpLine);
tmpLine.Location.Y += baseLinesDistanceScale * maxHeight;
}
// Check amount of lines
if (outputLines.IsEmpty())
return;
float totalHeight = tmpLine.Location.Y;
Float2 offset = Float2::Zero;
if (layout.VerticalAlignment == TextAlignment::Center)
{
offset.Y += (layout.Bounds.GetHeight() - totalHeight) * 0.5f;
}
else if (layout.VerticalAlignment == TextAlignment::Far)
{
offset.Y += layout.Bounds.GetHeight() - totalHeight;
}
for (int32 i = 0; i < outputLines.Count(); i++)
{
FontLineCache& line = outputLines[i];
Float2 rootPos = line.Location + offset;
// Fix upper left line corner to match desire text alignment
if (layout.HorizontalAlignment == TextAlignment::Center)
{
rootPos.X += (layout.Bounds.GetWidth() - line.Size.X) * 0.5f;
}
else if (layout.HorizontalAlignment == TextAlignment::Far)
{
rootPos.X += layout.Bounds.GetWidth() - line.Size.X;
}
line.Location = rootPos;
}
}
Float2 Font::MeasureText(const StringView& text, const TextLayoutOptions& layout)
{
// Check if there is no need to do anything
@@ -432,6 +632,11 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo
return rootOffset + Float2(lines.Last().Size.X, static_cast<float>((lines.Count() - 1) * baseLinesDistance));
}
bool Font::ContainsChar(Char c)
{
return FT_Get_Char_Index(GetAsset()->GetFTFace(), c) > 0;
}
void Font::FlushFaceSize() const
{
// Set the character size

View File

@@ -339,6 +339,15 @@ public:
/// <param name="outputLines">The output lines list.</param>
void ProcessText(const StringView& text, Array<FontLineCache>& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout);
/// <summary>
/// Processes text to get cached lines for rendering.
/// </summary>
/// <param name="fonts">The font list.</param>
/// <param name="text">The input text.</param>
/// <param name="layout">The layout properties.</param>
/// <param name="outputLines">The output lines list.</param>
static void ProcessText(const Array<Font*>& fonts, const StringView& text, Array<FontLineCache>& outputLines, API_PARAM(Ref) const TextLayoutOptions& layout);
/// <summary>
/// Processes text to get cached lines for rendering.
/// </summary>
@@ -395,6 +404,15 @@ public:
/// <returns>The minimum size for that text and fot to render properly.</returns>
API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout);
/// <summary>
/// Measures minimum size of the rectangle that will be needed to draw given text.
/// </summary>
/// <param name="fonts">The fonts to render with.</param>
/// <param name="text">The input text to test.</param>
/// <param name="layout">The layout properties.</param>
/// <returns>The minimum size for that text and fot to render properly.</returns>
API_FUNCTION() static Float2 MeasureText(const Array<Font*>& fonts, const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout);
/// <summary>
/// Measures minimum size of the rectangle that will be needed to draw given text.
/// </summary>
@@ -482,6 +500,16 @@ public:
/// <returns>The character position (upper left corner which can be used for a caret position).</returns>
API_FUNCTION() Float2 GetCharPosition(const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout);
/// <summary>
/// Calculates character position for given text and character index.
/// </summary>
/// <param name="fonts">The fonts to use.</param>
/// <param name="text">The input text to test.</param>
/// <param name="index">The text position to get coordinates of.</param>
/// <param name="layout">The text layout properties.</param>
/// <returns>The character position (upper left corner which can be used for a caret position).</returns>
API_FUNCTION() static Float2 GetCharPosition(const Array<Font*>& fonts, const StringView& text, int32 index, API_PARAM(Ref) const TextLayoutOptions& layout);
/// <summary>
/// Calculates character position for given text and character index.
/// </summary>
@@ -518,6 +546,13 @@ public:
return GetCharPosition(textRange.Substring(text), index, TextLayoutOptions());
}
/// <summary>
/// Check if the font contains the glyph of a char
/// </summary>
/// <param name="c">The char to test.</param>
/// <returns>True if the font contains the glyph of the char, otherwise false.</returns>
API_FUNCTION() FORCE_INLINE bool ContainsChar(Char c);
/// <summary>
/// Flushes the size of the face with the Free Type library backend.
/// </summary>

View File

@@ -155,6 +155,9 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry)
// Get the index to the glyph in the font face
const FT_UInt glyphIndex = FT_Get_Char_Index(face, c);
if (glyphIndex == 0) {
LOG(Warning, "Font `{}` doesn't contain character `\\u{:x}`, consider choosing another font. ", String(face->family_name), c);
}
// Load the glyph
const FT_Error error = FT_Load_Glyph(face, glyphIndex, glyphFlags);

View File

@@ -27,11 +27,11 @@
#if USE_EDITOR
#define RENDER2D_CHECK_RENDERING_STATE \
if (!Render2D::IsRendering()) \
{ \
LOG(Error, "Calling Render2D is only valid during rendering."); \
return; \
}
if (!Render2D::IsRendering()) \
{ \
LOG(Error, "Calling Render2D is only valid during rendering."); \
return; \
}
#else
#define RENDER2D_CHECK_RENDERING_STATE
#endif
@@ -54,7 +54,7 @@ const bool DownsampleForBlur = false;
PACK_STRUCT(struct Data {
Matrix ViewProjection;
});
});
PACK_STRUCT(struct BlurData {
Float2 InvBufferSize;
@@ -62,7 +62,7 @@ PACK_STRUCT(struct BlurData {
float Dummy0;
Float4 Bounds;
Float4 WeightAndOffsets[RENDER2D_BLUR_MAX_SAMPLES / 2];
});
});
enum class DrawCallType : byte
{
@@ -1174,7 +1174,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
drawCall.AsChar.Mat = nullptr;
}
Float2 pointer = location;
for (int32 currentIndex = 0; currentIndex <= text.Length(); currentIndex++)
for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++)
{
// Cache current character
const Char currentChar = text[currentIndex];
@@ -1368,6 +1368,318 @@ void Render2D::DrawText(Font* font, const StringView& text, const TextRange& tex
DrawText(font, textRange.Substring(text), color, layout, customMaterial);
}
void Render2D::DrawText(const Array<Font*>& fonts, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial)
{
RENDER2D_CHECK_RENDERING_STATE;
// Check if there is no need to do anything
if (fonts.IsEmpty() || text.Length() < 0)
return;
// Temporary data
uint32 fontAtlasIndex = 0;
FontTextureAtlas* fontAtlas = nullptr;
Float2 invAtlasSize = Float2::One;
FontCharacterEntry previous;
int32 kerning;
float scale = 1.0f / FontManager::FontScale;
// Render all characters
FontCharacterEntry entry;
Render2DDrawCall drawCall;
if (customMaterial)
{
drawCall.Type = DrawCallType::DrawCharMaterial;
drawCall.AsChar.Mat = customMaterial;
}
else
{
drawCall.Type = DrawCallType::DrawChar;
drawCall.AsChar.Mat = nullptr;
}
// The following code cut the text into segments, according to the font used to render
Float2 pointer = location;
// The starting index of the current segment
int32 startIndex = 0;
// The index of the font used by the current segment
int32 segmentFontIndex = 0;
// The maximum font height of the current line
float maxHeight = 0;
for (int32 currentIndex = 0; currentIndex <= text.Length(); currentIndex++)
{
// Cache current character
const Char currentChar = currentIndex < text.Length() ? text[currentIndex] : 0;
// Check if it isn't a newline character
if (currentChar != '\n')
{
int32 fontIndex = 0;
if (currentIndex < text.Length()) {
while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(currentChar))
{
fontIndex++;
}
// If no font can match the char, then use the segment font
if (fontIndex == fonts.Count()) {
fontIndex = segmentFontIndex;
}
// Do nothing if the char still belongs to the current segment
if (fontIndex == segmentFontIndex) {
continue;
}
}
// Render the pending segment before beginning the new segment
renderText:auto fontHeight = fonts[segmentFontIndex]->GetHeight();
maxHeight = Math::Max(maxHeight, static_cast<float>(fontHeight));
auto fontDescender = fonts[segmentFontIndex]->GetDescender();
for (int32 renderIndex = startIndex; renderIndex < currentIndex; renderIndex++)
{
// Get character entry
fonts[segmentFontIndex]->GetCharacter(text[renderIndex], entry);
// Check if need to select/change font atlas (since characters even in the same font may be located in different atlases)
if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex)
{
// Get texture atlas that contains current character
fontAtlasIndex = entry.TextureIndex;
fontAtlas = FontManager::GetAtlas(fontAtlasIndex);
if (fontAtlas)
{
fontAtlas->EnsureTextureCreated();
drawCall.AsChar.Tex = fontAtlas->GetTexture();
invAtlasSize = 1.0f / fontAtlas->GetSize();
}
else
{
drawCall.AsChar.Tex = nullptr;
invAtlasSize = 1.0f;
}
}
// Check if character is a whitespace
const bool isWhitespace = StringUtils::IsWhitespace(text[renderIndex]);
// Get kerning
if (!isWhitespace && previous.IsValid)
{
kerning = fonts[segmentFontIndex]->GetKerning(previous.Character, entry.Character);
}
else
{
kerning = 0;
}
pointer.X += kerning * scale;
previous = entry;
// Omit whitespace characters
if (!isWhitespace)
{
// Calculate character size and atlas coordinates
const float x = pointer.X + entry.OffsetX * scale;
const float y = pointer.Y + (fontHeight + fontDescender - entry.OffsetY) * scale;
Rectangle charRect(x, y, entry.UVSize.X * scale, entry.UVSize.Y * scale);
Float2 upperLeftUV = entry.UV * invAtlasSize;
Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize;
// Add draw call
drawCall.StartIB = IBIndex;
drawCall.CountIB = 6;
DrawCalls.Add(drawCall);
WriteRect(charRect, color, upperLeftUV, rightBottomUV);
}
// Move
pointer.X += entry.AdvanceX * scale;
}
// Start new segment
startIndex = currentIndex;
segmentFontIndex = fontIndex;
if (currentIndex == text.Length() - 1) {
currentIndex++;
goto renderText;
}
}
else
{
// Move
pointer.X = location.X;
pointer.Y += maxHeight * scale;
// Clear max height
maxHeight = 0;
}
}
}
void Render2D::DrawText(const Array<Font*>& fonts, const StringView& text, const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial)
{
DrawText(fonts, textRange.Substring(text), color, location, customMaterial);
}
void Render2D::DrawText(const Array<Font*>& fonts, const StringView& text, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial)
{
RENDER2D_CHECK_RENDERING_STATE;
// Check if there is no need to do anything
if (fonts.IsEmpty() || text.IsEmpty() || layout.Scale <= ZeroTolerance)
return;
// Temporary data
uint32 fontAtlasIndex = 0;
FontTextureAtlas* fontAtlas = nullptr;
Float2 invAtlasSize = Float2::One;
FontCharacterEntry previous;
int32 kerning;
float scale = layout.Scale / FontManager::FontScale;
// Process text to get lines
Lines.Clear();
Font::ProcessText(fonts, text, Lines, layout);
// Render all lines
FontCharacterEntry entry;
Render2DDrawCall drawCall;
if (customMaterial)
{
drawCall.Type = DrawCallType::DrawCharMaterial;
drawCall.AsChar.Mat = customMaterial;
}
else
{
drawCall.Type = DrawCallType::DrawChar;
drawCall.AsChar.Mat = nullptr;
}
for (int32 lineIndex = 0; lineIndex < Lines.Count(); lineIndex++)
{
const FontLineCache& line = Lines[lineIndex];
// The following code cut the text into segments, according to the font used to render
Float2 pointer = line.Location;
// The starting index of the current segment
int32 startIndex = line.FirstCharIndex;
// The index of the font used by the current segment
int32 segmentFontIndex = 0;
// The maximum font height of the current line
float maxHeight = 0;
// Partition and render all characters from the line
for (int32 charIndex = line.FirstCharIndex; charIndex <= line.LastCharIndex + 1; charIndex++)
{
const Char c = charIndex <= line.LastCharIndex ? text[charIndex] : 0;
if (c != '\n')
{
int32 fontIndex = 0;
if (charIndex <= line.LastCharIndex) {
while (fontIndex < fonts.Count() && !fonts[fontIndex]->ContainsChar(c))
{
fontIndex++;
}
// If no font can match the char, then use the segment font
if (fontIndex == fonts.Count()) {
fontIndex = segmentFontIndex;
}
// Do nothing if the char still belongs to the current segment
if (fontIndex == segmentFontIndex) {
continue;
}
}
// Render the pending segment before beginning the new segment
auto fontHeight = fonts[segmentFontIndex]->GetHeight();
maxHeight = Math::Max(maxHeight, static_cast<float>(fontHeight));
auto fontDescender = fonts[segmentFontIndex]->GetDescender();
const Char* pred = L"Type";
if (text.Substring(0, Math::Min(4, text.Length())) == pred) {
// __debugbreak();
}
for (int32 renderIndex = startIndex; renderIndex < charIndex; renderIndex++)
{
// Get character entry
fonts[segmentFontIndex]->GetCharacter(text[renderIndex], entry);
// Check if need to select/change font atlas (since characters even in the same font may be located in different atlases)
if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex)
{
// Get texture atlas that contains current character
fontAtlasIndex = entry.TextureIndex;
fontAtlas = FontManager::GetAtlas(fontAtlasIndex);
if (fontAtlas)
{
fontAtlas->EnsureTextureCreated();
invAtlasSize = 1.0f / fontAtlas->GetSize();
drawCall.AsChar.Tex = fontAtlas->GetTexture();
}
else
{
invAtlasSize = 1.0f;
drawCall.AsChar.Tex = nullptr;
}
}
// Get kerning
const bool isWhitespace = StringUtils::IsWhitespace(text[renderIndex]);
if (!isWhitespace && previous.IsValid)
{
kerning = fonts[segmentFontIndex]->GetKerning(previous.Character, entry.Character);
}
else
{
kerning = 0;
}
pointer.X += (float)kerning * scale;
previous = entry;
// Omit whitespace characters
if (!isWhitespace)
{
// Calculate character size and atlas coordinates
const float x = pointer.X + entry.OffsetX * scale;
const float y = pointer.Y - entry.OffsetY * scale + Math::Ceil((fontHeight + fontDescender) * scale);
Rectangle charRect(x, y, entry.UVSize.X * scale, entry.UVSize.Y * scale);
charRect.Offset(layout.Bounds.Location);
Float2 upperLeftUV = entry.UV * invAtlasSize;
Float2 rightBottomUV = (entry.UV + entry.UVSize) * invAtlasSize;
// Add draw call
drawCall.StartIB = IBIndex;
drawCall.CountIB = 6;
DrawCalls.Add(drawCall);
WriteRect(charRect, color, upperLeftUV, rightBottomUV);
}
// Move
pointer.X += entry.AdvanceX * scale;
}
// Start new segment
startIndex = charIndex;
segmentFontIndex = fontIndex;
}
}
}
}
void Render2D::DrawText(const Array<Font*>& fonts, const StringView& text, const TextRange& textRange, const Color& color, const TextLayoutOptions& layout, MaterialBase* customMaterial)
{
DrawText(fonts, textRange.Substring(text), color, layout, customMaterial);
}
FORCE_INLINE bool NeedAlphaWithTint(const Color& color)
{
return (color.A * TintLayersStack.Peek().A) < 1.0f;
@@ -1931,7 +2243,7 @@ void Render2D::DrawBlur(const Rectangle& rect, float blurStrength)
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices, const Span<Float2>& uvs)
{
RENDER2D_CHECK_RENDERING_STATE;
CHECK(vertices.Length() == uvs.Length())
CHECK(vertices.Length() == uvs.Length());
Render2DDrawCall& drawCall = DrawCalls.AddOne();
drawCall.Type = DrawCallType::FillTexture;
@@ -1977,7 +2289,7 @@ void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<uint16>& indices,
drawCall.StartIB = IBIndex;
drawCall.CountIB = indices.Length();
drawCall.AsTexture.Ptr = t;
for (int32 i = 0; i < indices.Length();)
{
const uint16 i0 = indices.Get()[i++];

View File

@@ -152,6 +152,59 @@ namespace FlaxEngine
DrawText(font, text, color, ref layout, customMaterial);
}
/// <summary>
/// Draws a text.
/// </summary>
/// <param name="fonts">The fonts to use, ordered by priority.</param>
/// <param name="text">The text to render.</param>
/// <param name="layoutRect">The size and position of the area in which the text is drawn.</param>
/// <param name="color">The text color.</param>
/// <param name="horizontalAlignment">The horizontal alignment of the text in a layout rectangle.</param>
/// <param name="verticalAlignment">The vertical alignment of the text in a layout rectangle.</param>
/// <param name="textWrapping">Describes how wrap text inside a layout rectangle.</param>
/// <param name="baseLinesGapScale">The scale for distance one baseline from another. Default is 1.</param>
/// <param name="scale">The text drawing scale. Default is 1.</param>
public static void DrawText(Font[] fonts, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f)
{
var layout = new TextLayoutOptions
{
Bounds = layoutRect,
HorizontalAlignment = horizontalAlignment,
VerticalAlignment = verticalAlignment,
TextWrapping = textWrapping,
Scale = scale,
BaseLinesGapScale = baseLinesGapScale,
};
DrawText(fonts, text, color, ref layout);
}
/// <summary>
/// Draws a text using a custom material shader. Given material must have GUI domain and a public parameter named Font (texture parameter used for a font atlas sampling).
/// </summary>
/// <param name="fonts">The fonts to use, ordered by priority.</param>
/// <param name="customMaterial">Custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture.</param>
/// <param name="text">The text to render.</param>
/// <param name="layoutRect">The size and position of the area in which the text is drawn.</param>
/// <param name="color">The text color.</param>
/// <param name="horizontalAlignment">The horizontal alignment of the text in a layout rectangle.</param>
/// <param name="verticalAlignment">The vertical alignment of the text in a layout rectangle.</param>
/// <param name="textWrapping">Describes how wrap text inside a layout rectangle.</param>
/// <param name="baseLinesGapScale">The scale for distance one baseline from another. Default is 1.</param>
/// <param name="scale">The text drawing scale. Default is 1.</param>
public static void DrawText(Font[] fonts, MaterialBase customMaterial, string text, Rectangle layoutRect, Color color, TextAlignment horizontalAlignment = TextAlignment.Near, TextAlignment verticalAlignment = TextAlignment.Near, TextWrapping textWrapping = TextWrapping.NoWrap, float baseLinesGapScale = 1.0f, float scale = 1.0f)
{
var layout = new TextLayoutOptions
{
Bounds = layoutRect,
HorizontalAlignment = horizontalAlignment,
VerticalAlignment = verticalAlignment,
TextWrapping = textWrapping,
Scale = scale,
BaseLinesGapScale = baseLinesGapScale,
};
DrawText(fonts, text, color, ref layout, customMaterial);
}
/// <summary>
/// Calls drawing GUI to the texture.
/// </summary>

View File

@@ -33,7 +33,7 @@ API_CLASS(Static) class FLAXENGINE_API Render2D
/// <summary>
/// The rendering features and options flags.
/// </summary>
API_ENUM(Attributes="Flags") enum class RenderingFeatures
API_ENUM(Attributes = "Flags") enum class RenderingFeatures
{
/// <summary>
/// The none.
@@ -215,6 +215,49 @@ public:
/// <param name="customMaterial">The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture.</param>
API_FUNCTION() static void DrawText(Font* font, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr);
/// <summary>
/// Draws a text.
/// </summary>
/// <param name="fonts">The fonts to use, ordered by priority.</param>
/// <param name="text">The text to render.</param>
/// <param name="textRange">The input text range (substring range of the input text parameter).</param>
/// <param name="color">The text color.</param>
/// <param name="location">The text location.</param>
/// <param name="customMaterial">The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture.</param>
API_FUNCTION() static void DrawText(const Array<Font*, HeapAllocation>& fonts, const StringView& text, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr);
/// <summary>
/// Draws a text with formatting.
/// </summary>
/// <param name="fonts">The fonts to use, ordered by priority.</param>
/// <param name="text">The text to render.</param>
/// <param name="color">The text color.</param>
/// <param name="layout">The text layout properties.</param>
/// <param name="customMaterial">The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture.</param>
API_FUNCTION() static void DrawText(const Array<Font*, HeapAllocation>& fonts, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, const Float2& location, MaterialBase* customMaterial = nullptr);
/// <summary>
/// Draws a text with formatting.
/// </summary>
/// <param name="fonts">The fonts to use, ordered by priority.</param>
/// <param name="text">The text to render.</param>
/// <param name="textRange">The input text range (substring range of the input text parameter).</param>
/// <param name="color">The text color.</param>
/// <param name="layout">The text layout properties.</param>
/// <param name="customMaterial">The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture.</param>
API_FUNCTION() static void DrawText(const Array<Font*, HeapAllocation>& fonts, const StringView& text, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr);
/// <summary>
/// Draws a text with formatting.
/// </summary>
/// <param name="fonts">The fonts to use, ordered by priority.</param>
/// <param name="text">The text to render.</param>
/// <param name="textRange">The input text range (substring range of the input text parameter).</param>
/// <param name="color">The text color.</param>
/// <param name="layout">The text layout properties.</param>
/// <param name="customMaterial">The custom material for font characters rendering. It must contain texture parameter named Font used to sample font texture.</param>
API_FUNCTION() static void DrawText(const Array<Font*, HeapAllocation>& fonts, const StringView& text, API_PARAM(Ref) const TextRange& textRange, const Color& color, API_PARAM(Ref) const TextLayoutOptions& layout, MaterialBase* customMaterial = nullptr);
/// <summary>
/// Fills a rectangle area.
/// </summary>

View File

@@ -295,12 +295,13 @@ namespace FlaxEngine
// Use optionally bundled default font (matches Editor)
var defaultFont = Content.LoadAsyncInternal<FontAsset>("Editor/Fonts/Roboto-Regular");
var cjkFont = Content.LoadAsyncInternal<FontAsset>("NotoSansSC-Medium");
if (defaultFont)
{
style.FontTitle = defaultFont.CreateFont(18);
style.FontLarge = defaultFont.CreateFont(14);
style.FontMedium = defaultFont.CreateFont(9);
style.FontSmall = defaultFont.CreateFont(9);
style.FontMedium = new Font[] { defaultFont.CreateFont(9), cjkFont.CreateFont(9) };
style.FontSmall = new Font[] { defaultFont.CreateFont(9), cjkFont.CreateFont(9) };
}
Style.Current = style;

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Linq;
namespace FlaxEngine.GUI
{
@@ -155,7 +156,7 @@ namespace FlaxEngine.GUI
var style = Style.Current;
if (style != null)
{
_font = new FontReference(style.FontMedium);
_font = new FontReference(style.FontMedium.First());
TextColor = style.Foreground;
BackgroundColor = style.BackgroundNormal;
BorderColor = style.BorderNormal;

View File

@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace FlaxEngine.GUI
{
@@ -358,7 +359,7 @@ namespace FlaxEngine.GUI
: base(0, 0, 120, 18.0f)
{
var style = Style.Current;
Font = new FontReference(style.FontMedium);
Font = new FontReference(style.FontMedium.First());
TextColor = style.Foreground;
BackgroundColor = style.BackgroundNormal;
BackgroundColorHighlighted = BackgroundColor;

View File

@@ -1,6 +1,8 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using FlaxEditor.Options;
using System.ComponentModel;
using System.Linq;
namespace FlaxEngine.GUI
{
@@ -190,7 +192,7 @@ namespace FlaxEngine.GUI
{
AutoFocus = false;
var style = Style.Current;
Font = new FontReference(style.FontMedium);
Font = new FontReference(style.FontMedium.First());
TextColor = style.Foreground;
TextColorHighlighted = style.Foreground;
}
@@ -201,7 +203,7 @@ namespace FlaxEngine.GUI
{
AutoFocus = false;
var style = Style.Current;
Font = new FontReference(style.FontMedium);
Font = new FontReference(style.FontMedium.First());
TextColor = style.Foreground;
TextColorHighlighted = style.Foreground;
}
@@ -233,7 +235,7 @@ namespace FlaxEngine.GUI
}
}
Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale);
Render2D.DrawText(new Font[] { _font.GetFont(), Style.Current.FontCJK }, Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale);
if (ClipText)
Render2D.PopClip();

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
using System.Linq;
namespace FlaxEngine.GUI
{
@@ -45,7 +46,7 @@ namespace FlaxEngine.GUI
var style = Style.Current;
_textStyle = new TextBlockStyle
{
Font = new FontReference(style.FontMedium),
Font = new FontReference(style.FontMedium.First()),
Color = style.Foreground,
BackgroundSelectedBrush = new SolidColorBrush(style.BackgroundSelected),
};

View File

@@ -1,5 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System.Linq;
namespace FlaxEngine.GUI
{
/// <summary>
@@ -88,7 +90,7 @@ namespace FlaxEngine.GUI
_layout.Bounds = new Rectangle(DefaultMargin, 1, Width - 2 * DefaultMargin, Height - 2);
var style = Style.Current;
Font = new FontReference(style.FontMedium);
Font = new FontReference(style.FontMedium.First());
TextColor = style.Foreground;
WatermarkTextColor = style.ForegroundDisabled;
SelectionColor = style.BackgroundSelected;

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Linq;
namespace FlaxEngine.GUI
{
@@ -237,7 +238,7 @@ namespace FlaxEngine.GUI
var style = Style.Current;
HeaderColor = style.BackgroundNormal;
HeaderColorMouseOver = style.BackgroundHighlighted;
HeaderTextFont = new FontReference(style.FontMedium);
HeaderTextFont = new FontReference(style.FontMedium.First());
HeaderTextColor = style.Foreground;
ArrowImageOpened = new SpriteBrush(style.ArrowDown);
ArrowImageClosed = new SpriteBrush(style.ArrowRight);

View File

@@ -1,5 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System.Linq;
namespace FlaxEngine.GUI
{
/// <summary>
@@ -12,6 +14,14 @@ namespace FlaxEngine.GUI
/// </summary>
public static Style Current { get; set; }
public Font FontCJK
{
get => _fontCJK?.GetFont();
set => _fontCJK = new FontReference(value);
}
private FontReference _fontCJK;
[Serialize]
private FontReference _fontTitle;
@@ -41,31 +51,31 @@ namespace FlaxEngine.GUI
}
[Serialize]
private FontReference _fontMedium;
private FontReference[] _fontMedium;
/// <summary>
/// The font medium.
/// </summary>
[NoSerialize]
[EditorOrder(30)]
public Font FontMedium
public Font[] FontMedium
{
get => _fontMedium?.GetFont();
set => _fontMedium = new FontReference(value);
get => _fontMedium?.Select((x)=>x.GetFont()).ToArray();
set => _fontMedium = value.Select((x)=>new FontReference(x)).ToArray();
}
[Serialize]
private FontReference _fontSmall;
private FontReference[] _fontSmall;
/// <summary>
/// The font small.
/// </summary>
[NoSerialize]
[EditorOrder(40)]
public Font FontSmall
public Font[] FontSmall
{
get => _fontSmall?.GetFont();
set => _fontSmall = new FontReference(value);
get => _fontSmall?.Select((x) => x.GetFont()).ToArray();
set => _fontSmall = value.Select((x) => new FontReference(x)).ToArray();
}
/// <summary>

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Linq;
namespace FlaxEngine.GUI
{
@@ -255,14 +256,14 @@ namespace FlaxEngine.GUI
// Calculate size of the tooltip
var size = Float2.Zero;
if (style != null && style.FontMedium && !string.IsNullOrEmpty(_currentText))
if (style != null && style.FontMedium.First() && !string.IsNullOrEmpty(_currentText))
{
var layout = TextLayoutOptions.Default;
layout.Bounds = new Rectangle(0, 0, MaxWidth, 10000000);
layout.HorizontalAlignment = TextAlignment.Center;
layout.VerticalAlignment = TextAlignment.Center;
layout.TextWrapping = TextWrapping.WrapWords;
var items = style.FontMedium.ProcessText(_currentText, ref layout);
var items = style.FontMedium.First().ProcessText(_currentText, ref layout);
for (int i = 0; i < items.Length; i++)
{
ref var item = ref items[i];