Add text block alignment options for rich text box (and valign tag)
This commit is contained in:
@@ -90,6 +90,7 @@ namespace FlaxEngine.GUI
|
||||
{ "i", ProcessItalic },
|
||||
{ "size", ProcessSize },
|
||||
{ "img", ProcessImage },
|
||||
{ "valign", ProcessVAlign },
|
||||
};
|
||||
|
||||
private HtmlParser _parser = new HtmlParser();
|
||||
@@ -132,8 +133,13 @@ namespace FlaxEngine.GUI
|
||||
OnParseTag(ref context, ref tag);
|
||||
}
|
||||
|
||||
// Insert remaining text
|
||||
// Insert remaining text (and line)
|
||||
OnAddTextBlock(ref context, testStartPos, _text.Length);
|
||||
if (context.LineStartTextBlockIndex != _textBlocks.Count)
|
||||
{
|
||||
context.Caret.X = 0;
|
||||
OnLineAdded(ref context, _text.Length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -224,21 +230,54 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
ref TextBlock textBlock = ref textBlocks[i];
|
||||
var textBlockSize = textBlock.Bounds.BottomRight - lineOrigin;
|
||||
var textBlockFont = textBlock.Style.Font.GetFont();
|
||||
if (textBlockFont)
|
||||
lineAscender = Mathf.Max(lineAscender, textBlockFont.Ascender);
|
||||
var ascender = textBlock.Ascender;
|
||||
//if (ascender <= 0)
|
||||
{
|
||||
var textBlockFont = textBlock.Style.Font.GetFont();
|
||||
if (textBlockFont)
|
||||
ascender = textBlockFont.Ascender;
|
||||
}
|
||||
lineAscender = Mathf.Max(lineAscender, ascender);
|
||||
lineSize = Float2.Max(lineSize, textBlockSize);
|
||||
}
|
||||
|
||||
// Organize text blocks to match the baseline of the line (use ascender)
|
||||
// Organize text blocks within line
|
||||
for(int i = context.LineStartTextBlockIndex; i < _textBlocks.Count; i++)
|
||||
{
|
||||
ref TextBlock textBlock = ref textBlocks[i];
|
||||
var offset = lineSize.Y - textBlock.Bounds.Height;
|
||||
var textBlockFont = textBlock.Style.Font.GetFont();
|
||||
if (textBlockFont)
|
||||
offset = lineAscender - textBlockFont.Ascender;
|
||||
textBlock.Bounds.Location.Y += offset;
|
||||
var vOffset = lineSize.Y - textBlock.Bounds.Height;
|
||||
switch (textBlock.Style.Alignment & TextBlockStyle.Alignments.VerticalMask)
|
||||
{
|
||||
case TextBlockStyle.Alignments.Baseline:
|
||||
{
|
||||
// Match the baseline of the line (use ascender)
|
||||
var ascender = textBlock.Ascender;
|
||||
if (ascender <= 0)
|
||||
{
|
||||
var textBlockFont = textBlock.Style.Font.GetFont();
|
||||
if (textBlockFont)
|
||||
ascender = textBlockFont.Ascender;
|
||||
}
|
||||
vOffset = lineAscender - ascender;
|
||||
textBlock.Bounds.Location.Y += vOffset;
|
||||
break;
|
||||
}
|
||||
case TextBlockStyle.Alignments.Top:
|
||||
{
|
||||
textBlock.Bounds.Location.Y = lineOrigin.Y;
|
||||
break;
|
||||
}
|
||||
case TextBlockStyle.Alignments.Middle:
|
||||
{
|
||||
textBlock.Bounds.Location.Y = lineOrigin.Y + vOffset * 0.5f;
|
||||
break;
|
||||
}
|
||||
case TextBlockStyle.Alignments.Bottom:
|
||||
{
|
||||
textBlock.Bounds.Location.Y = lineOrigin.Y + vOffset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move to the next line
|
||||
|
||||
@@ -176,17 +176,60 @@ namespace FlaxEngine.GUI
|
||||
var font = imageBlock.Style.Font.GetFont();
|
||||
if (font)
|
||||
imageBlock.Bounds.Size = new Float2(font.Height);
|
||||
imageBlock.Bounds.Size.X *= image.Size.X / image.Size.Y; // Keep aspect ration
|
||||
TryParseNumberTag(ref tag, "width", imageBlock.Bounds.Width, out var width);
|
||||
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;
|
||||
TryParseNumberTag(ref tag, "height", imageBlock.Bounds.Height, out var height);
|
||||
bool hasHeight = TryParseNumberTag(ref tag, "height", imageBlock.Bounds.Height, out var height);
|
||||
imageBlock.Bounds.Height = height;
|
||||
if ((hasHeight || hasWidth) && (hasWidth != hasHeight))
|
||||
{
|
||||
// Maintain aspect ratio after scaling by just width or height
|
||||
if (hasHeight)
|
||||
imageBlock.Bounds.Size.X = imageBlock.Bounds.Size.Y * image.Size.X / image.Size.Y;
|
||||
else
|
||||
imageBlock.Bounds.Size.Y = imageBlock.Bounds.Size.X * image.Size.Y / image.Size.X;
|
||||
}
|
||||
TryParseNumberTag(ref tag, "scale", 1.0f, out var scale);
|
||||
imageBlock.Bounds.Size *= scale;
|
||||
|
||||
|
||||
// Image height defines the ascender so it's placed on the baseline by default
|
||||
imageBlock.Ascender = imageBlock.Bounds.Size.Y;
|
||||
|
||||
context.AddTextBlock(ref imageBlock);
|
||||
context.Caret.X += imageBlock.Bounds.Size.X;
|
||||
}
|
||||
|
||||
private static void ProcessVAlign(ref ParsingContext context, ref HtmlTag tag)
|
||||
{
|
||||
if (tag.IsSlash)
|
||||
{
|
||||
context.StyleStack.Pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
var style = context.StyleStack.Peek();
|
||||
if (tag.Attributes.TryGetValue(string.Empty, out var valign))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
context.StyleStack.Push(style);
|
||||
}
|
||||
}
|
||||
|
||||
private static Asset FindAsset(string name, System.Type type)
|
||||
{
|
||||
@@ -201,16 +244,19 @@ namespace FlaxEngine.GUI
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void TryParseNumberTag(ref HtmlTag tag, string name, float input, out float output)
|
||||
private static bool TryParseNumberTag(ref HtmlTag tag, string name, float input, out float output)
|
||||
{
|
||||
output = input;
|
||||
bool used = false;
|
||||
if (tag.Attributes.TryGetValue(name, out var text))
|
||||
{
|
||||
if (float.TryParse(text, out var width))
|
||||
output = width;
|
||||
if (text.Length > 1 && text[text.Length - 1] == '%')
|
||||
output = input * float.Parse(text.Substring(0, text.Length - 1)) / 100.0f;
|
||||
used = true;
|
||||
}
|
||||
return used;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@ namespace FlaxEngine.GUI
|
||||
/// </summary>
|
||||
public Rectangle Bounds;
|
||||
|
||||
/// <summary>
|
||||
/// Custom ascender value for the line layout (block size above the baseline). Set to 0 to use ascender from the font.
|
||||
/// </summary>
|
||||
public float Ascender;
|
||||
|
||||
/// <summary>
|
||||
/// The custom tag.
|
||||
/// </summary>
|
||||
|
||||
@@ -7,52 +7,90 @@ namespace FlaxEngine.GUI
|
||||
/// </summary>
|
||||
public struct TextBlockStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Text block alignments modes.
|
||||
/// </summary>
|
||||
public enum Alignments
|
||||
{
|
||||
/// <summary>
|
||||
/// Block will be aligned to the baseline of the text (vertically).
|
||||
/// </summary>
|
||||
Baseline,
|
||||
|
||||
/// <summary>
|
||||
/// Block will be aligned to the top edge of the line (vertically).
|
||||
/// </summary>
|
||||
Top = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Block will be aligned to center of the line (vertically).
|
||||
/// </summary>
|
||||
Middle = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Block will be aligned to the bottom edge of the line (vertically).
|
||||
/// </summary>
|
||||
Bottom = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Mask with vertical alignment flags.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
VerticalMask = Top | Middle | Bottom,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The text font.
|
||||
/// </summary>
|
||||
[EditorOrder(0), Tooltip("The text font.")]
|
||||
[EditorOrder(0)]
|
||||
public FontReference Font;
|
||||
|
||||
/// <summary>
|
||||
/// The custom material for the text rendering (must be GUI domain).
|
||||
/// </summary>
|
||||
[EditorOrder(10), Tooltip("The custom material for the text rendering (must be GUI domain).")]
|
||||
[EditorOrder(10)]
|
||||
public MaterialBase CustomMaterial;
|
||||
|
||||
/// <summary>
|
||||
/// The text color (tint and opacity).
|
||||
/// </summary>
|
||||
[EditorOrder(20), Tooltip("The text color (tint and opacity).")]
|
||||
[EditorOrder(20)]
|
||||
public Color Color;
|
||||
|
||||
/// <summary>
|
||||
/// The text shadow color (tint and opacity). Set to transparent to disable shadow drawing.
|
||||
/// </summary>
|
||||
[EditorOrder(30), Tooltip("The text shadow color (tint and opacity). Set to transparent to disable shadow drawing.")]
|
||||
[EditorOrder(30)]
|
||||
public Color ShadowColor;
|
||||
|
||||
/// <summary>
|
||||
/// The text shadow offset from the text location. Set to zero to disable shadow drawing.
|
||||
/// </summary>
|
||||
[EditorOrder(40), Tooltip("The text shadow offset from the text location. Set to zero to disable shadow drawing.")]
|
||||
[EditorOrder(40)]
|
||||
public Float2 ShadowOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The background brush for the text range.
|
||||
/// </summary>
|
||||
[EditorOrder(45), Tooltip("The background brush for the text range.")]
|
||||
[EditorOrder(45)]
|
||||
public IBrush BackgroundBrush;
|
||||
|
||||
/// <summary>
|
||||
/// The background brush for the selected text range.
|
||||
/// </summary>
|
||||
[EditorOrder(50), Tooltip("The background brush for the selected text range.")]
|
||||
[EditorOrder(50)]
|
||||
public IBrush BackgroundSelectedBrush;
|
||||
|
||||
/// <summary>
|
||||
/// The underline line brush.
|
||||
/// </summary>
|
||||
[EditorOrder(60), Tooltip("The underline line brush.")]
|
||||
[EditorOrder(60)]
|
||||
public IBrush UnderlineBrush;
|
||||
|
||||
/// <summary>
|
||||
/// The text block alignment.
|
||||
/// </summary>
|
||||
[EditorOrder(100)]
|
||||
public Alignments Alignment;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user